If you're old enough to remember Internet Explorer, CSS shim files, and spending way too much time on Newgrounds, first off—how are your knees these days? But secondly, you probably remember Flash Player and Silverlight as being the way you watched video in web browsers. Those were the days of browser plugins, mysterious crashes, and that wonderful "Click to enable Flash" banner that haunted every second website.
But since 2008, we've had the option to use the native <video> HTML5 element to handle video playback and streaming. No more plugins. No more Adobe update prompts. And here's the thing that might surprise you: we don't even need a modern framework to do this.
The requirements to play with the <video> element are as simple as any web browser on a modern operating system, and an ImageKit account to host your video files. We'll also cover some fun upgrades and extras you can do later on with JavaScript and ImageKit's video API.
See it in action
If you want to skip ahead and see the finished result, I've got you covered:
- Live demo: https://elsmore.me/imagekit-javascript-video-streaming/
- Source code: https://github.com/ukmadlz/imagekit-astro-video-streaming
Feel free to poke around the code, fork it, break it, and make it your own. Now let's walk through how it all works.
Table of Contents
- Using the HTML5
<video>element - Adding playback controls
- Defining multiple video sources
- Displaying a video poster (thumbnail)
- Auto-playing videos the right way
- Building a custom video player with JavaScript
- Adaptive Bitrate Streaming (ABS)
- Using Video.js for advanced streaming
- Optimising video delivery with ImageKit
- Conclusion
Using the HTML5 <video> element
To start displaying a video on any given webpage is as easy as this:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>An Example HTML5 Video</title>
</head>
<body>
<video
src="https://ik.imagekit.io/ikmedia/example_video.mp4"
width="640"
height="360"
/>
</body>
</html>
That's right—just like an <img> tag takes a source location to display an image, the only thing the <video> tag strictly needs is a file passed into the src attribute. And just like an <img>, it's best to assign both a width and height to the <video> element to avoid layout shifts as the video loads.
If you're thinking "wait, that's it?"—yes, that's genuinely it for the bare minimum. But of course, we're going to make it better.
Adding playback controls
That bare-bones example above will display a video, but your users won't be able to do much with it. They can't play, pause, adjust volume, or seek through the timeline. Let's fix that:
<video
src="https://ik.imagekit.io/ikmedia/example_video.mp4"
width="640"
height="360"
controls
/>
The controls attribute adds the browser's default video controls. This includes play/pause, a progress bar, volume control, and fullscreen toggle. Each browser renders these slightly differently (Firefox, Chrome, and Safari all have their own flavour), but they're functional across the board.
Here are some other useful attributes you'll want to know about:
| Attribute | What it does |
|---|---|
controls |
Shows play, pause, volume, progress bar |
autoplay |
Starts playing automatically (with caveats) |
muted |
Starts the video muted |
loop |
Loops the video when it ends |
playsinline |
Plays inline on iOS instead of fullscreen |
preload |
Hints how much to preload (none, metadata, auto) |
poster |
Shows an image before the video plays |
Defining multiple video sources
Not all browsers support all video formats. While MP4 (H.264) is nearly universal these days, WebM can offer better compression, and you might have legacy content in other formats. The <source> element lets you provide fallbacks:
<video width="640" height="360" controls>
<source src="https://ik.imagekit.io/ikmedia/example_video.webm" type="video/webm">
<source src="https://ik.imagekit.io/ikmedia/example_video.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>
The browser reads top to bottom and picks the first format it supports. So if you want users to get WebM (smaller files, same quality), put that first. MP4 acts as the fallback.
The text between the tags ("Your browser does not support...") only displays if the browser doesn't support the <video> element at all. This is increasingly rare, but it's good practice to include it.
Pro tip: With ImageKit, you can transform video formats on the fly using URL parameters. Instead of hosting multiple files, use a single source and let ImageKit handle the conversion:
<video width="640" height="360" controls>
<source src="https://ik.imagekit.io/ikmedia/example_video.mp4?tr=f-webm" type="video/webm">
<source src="https://ik.imagekit.io/ikmedia/example_video.mp4?tr=f-mp4" type="video/mp4">
</video>
Displaying a video poster (thumbnail)
Before your video loads, you'll want to show something other than a black rectangle. The poster attribute lets you display an image:
<video
src="https://ik.imagekit.io/ikmedia/example_video.mp4"
width="640"
height="360"
controls
poster="https://ik.imagekit.io/ikmedia/example_video.mp4/ik-thumbnail.jpg"
/>
ImageKit has a neat trick here: you can generate thumbnails automatically from your video using the /ik-thumbnail.jpg suffix. Want a thumbnail from a specific timestamp? Add a transformation parameter:
https://ik.imagekit.io/ikmedia/example_video.mp4/ik-thumbnail.jpg?tr=so-5
This grabs a frame from 5 seconds into the video. You can also resize it:
https://ik.imagekit.io/ikmedia/example_video.mp4/ik-thumbnail.jpg?tr=so-5,w-640,h-360
No need to manually create and host poster images—let the API do the work.
Auto-playing videos the right way
Autoplay is a minefield. Browsers have cracked down hard on autoplaying videos because nobody likes visiting a site and having sound blast at them unexpectedly. Here's how to do it properly:
<video
src="https://ik.imagekit.io/ikmedia/example_video.mp4"
width="640"
height="360"
autoplay
muted
playsinline
/>
The key points:
-
autoplayalone won't work in most browsers - they require the video to also bemuted -
playsinlineis essential for iOS - without it, Safari on mobile opens videos in fullscreen mode - Consider your users - autoplaying videos (even muted) can be jarring
This pattern is perfect for hero videos, background loops, or product demos where you don't need audio. If you do need audio, you'll need to wait for user interaction first (like a click) before calling video.play() in JavaScript.
Building a custom video player with JavaScript
The native controls are fine, but sometimes you want something custom. The HTML5 video element exposes a comprehensive JavaScript API for building your own player. Here's a basic example:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Custom Video Player</title>
<style>
.video-container {
max-width: 640px;
margin: 0 auto;
}
video {
width: 100%;
display: block;
}
.controls {
display: flex;
gap: 10px;
padding: 10px;
background: #222;
align-items: center;
}
.controls button {
padding: 8px 16px;
cursor: pointer;
}
.progress-bar {
flex-grow: 1;
height: 8px;
background: #555;
cursor: pointer;
border-radius: 4px;
}
.progress {
height: 100%;
background: #4CAF50;
width: 0%;
border-radius: 4px;
}
.time {
color: white;
font-family: monospace;
}
</style>
</head>
<body>
<div class="video-container">
<video id="video" src="https://ik.imagekit.io/ikmedia/example_video.mp4"></video>
<div class="controls">
<button id="playPause">Play</button>
<div class="progress-bar" id="progressBar">
<div class="progress" id="progress"></div>
</div>
<span class="time" id="time">0:00 / 0:00</span>
</div>
</div>
<script>
const video = document.getElementById('video');
const playPauseBtn = document.getElementById('playPause');
const progressBar = document.getElementById('progressBar');
const progress = document.getElementById('progress');
const timeDisplay = document.getElementById('time');
// Play/Pause toggle
playPauseBtn.addEventListener('click', () => {
if (video.paused) {
video.play();
playPauseBtn.textContent = 'Pause';
} else {
video.pause();
playPauseBtn.textContent = 'Play';
}
});
// Update progress bar
video.addEventListener('timeupdate', () => {
const percentage = (video.currentTime / video.duration) * 100;
progress.style.width = percentage + '%';
timeDisplay.textContent = formatTime(video.currentTime) + ' / ' + formatTime(video.duration);
});
// Click to seek
progressBar.addEventListener('click', (e) => {
const rect = progressBar.getBoundingClientRect();
const percentage = (e.clientX - rect.left) / rect.width;
video.currentTime = percentage * video.duration;
});
// Format seconds to M:SS
function formatTime(seconds) {
if (isNaN(seconds)) return '0:00';
const mins = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60).toString().padStart(2, '0');
return mins + ':' + secs;
}
</script>
</body>
</html>
The video element gives you access to a wealth of properties and methods:
Properties:
-
video.currentTime- current playback position in seconds -
video.duration- total duration in seconds -
video.paused- boolean indicating if video is paused -
video.volume- volume level from 0.0 to 1.0 -
video.playbackRate- playback speed (1.0 is normal)
Methods:
-
video.play()- start playback (returns a Promise) -
video.pause()- pause playback -
video.load()- reload the video source
Events:
-
play- fired when playback starts -
pause- fired when playback pauses -
timeupdate- fired as playback progresses -
ended- fired when video reaches the end -
loadedmetadata- fired when duration/dimensions are available
This gives you everything you need to build players that match your brand, add custom features, or integrate with your application's state management.
Adaptive Bitrate Streaming (ABS)
Here's where things get interesting. So far, we've been dealing with single video files—one quality level, one file size, one experience for everyone. But what happens when someone's on a dodgy coffee shop WiFi versus a fibre connection at home?
Adaptive Bitrate Streaming (ABS) solves this by encoding your video at multiple quality levels and letting the player switch between them based on network conditions. The video starts quickly at lower quality, then upgrades as bandwidth allows. If the connection drops, it downgrades gracefully instead of buffering endlessly.
There are two main protocols:
-
HLS (HTTP Live Streaming) - Apple's format, uses
.m3u8playlist files -
MPEG-DASH (Dynamic Adaptive Streaming over HTTP) - Uses
.mpdmanifest files
The catch? The native <video> element doesn't support these formats directly in most browsers. Safari supports HLS natively (it's Apple's thing, after all), but Chrome and Firefox don't. That's where JavaScript libraries come in.
Using Video.js for advanced streaming
Video.js is an open-source HTML5 video player that handles ABS protocols, provides a consistent UI across browsers, and offers a plugin ecosystem for extended functionality.
First, include Video.js in your project:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Video.js Streaming Example</title>
<link href="https://vjs.zencdn.net/8.10.0/video-js.css" rel="stylesheet">
</head>
<body>
<video
id="my-video"
class="video-js vjs-default-skin"
controls
width="640"
height="360"
>
<source src="https://ik.imagekit.io/demo/sample-video.mp4/ik-master.mpd?tr=sr-240_360_480_720_1080" type="application/dash+xml">
</video>
<script src="https://vjs.zencdn.net/8.10.0/video.min.js"></script>
<script>
const player = videojs('my-video', {
fluid: true, // Responsive sizing
playbackRates: [0.5, 1, 1.5, 2], // Speed options
});
</script>
</body>
</html>
For HLS streams, just swap the source:
<source src="https://ik.imagekit.io/demo/sample-video.mp4/ik-master.m3u8?tr=sr-240_360_480_720_1080" type="application/x-mpegURL">
Video.js (version 7+) handles both DASH and HLS out of the box, no additional plugins required. The player will automatically select the appropriate quality based on the viewer's bandwidth.
Quality selection
Video.js also lets users manually select quality levels if they prefer. You can add this functionality with the quality selector plugin, or let the player handle it automatically—most users won't need to touch it.
Optimising video delivery with ImageKit
I've been dropping ImageKit URLs throughout this post, so let's properly cover what it can do for your video streaming setup.
Resize videos to appropriate dimensions
Why send a 4K video to someone watching on a 400px container? ImageKit lets you resize on the fly:
https://ik.imagekit.io/ikmedia/example_video.mp4?tr=w-640,h-360
This doesn't just scale the player—it actually delivers a smaller file. A video at the original 4K resolution might be 20MB, while the same video resized to 640x360 could be under 2MB. Same visual quality for your use case, massively reduced bandwidth.
Automatic format selection
Instead of manually specifying WebM and MP4 sources, let ImageKit figure out what the browser supports:
<video src="https://ik.imagekit.io/ikmedia/example_video.mp4?tr=f-auto" controls></video>
The f-auto transformation detects the viewer's browser and delivers the most optimised format automatically.
Generate ABS playlists
Creating DASH and HLS playlists normally requires encoding your video at multiple bitrates, generating segment files, and creating manifest files. ImageKit does all of this from a single source video:
DASH:
https://ik.imagekit.io/ikmedia/example_video.mp4/ik-master.mpd?tr=sr-240_360_480_720_1080
HLS:
https://ik.imagekit.io/ikmedia/example_video.mp4/ik-master.m3u8?tr=sr-240_360_480_720_1080
The sr-240_360_480_720_1080 parameter specifies which quality levels to include. ImageKit handles the encoding and playlist generation on demand.
Add overlays
Need to watermark your videos or add captions? ImageKit supports text and image overlays:
https://ik.imagekit.io/ikmedia/example_video.mp4?tr=l-text,i-My%20Company,fs-40,co-white,bg-00000080,pa-20,l-end
This adds "My Company" as a text overlay with a semi-transparent black background.
Conclusion
Video streaming with JavaScript and HTML5 has come a long way since the Flash days. The native <video> element handles most common use cases without any external dependencies. When you need adaptive streaming for varying network conditions, Video.js provides a solid foundation that works across browsers.
The real power comes from combining these client-side capabilities with a smart CDN like ImageKit. Instead of pre-encoding dozens of video variants, you can transform and deliver optimised video on the fly. Format conversion, resizing, thumbnail generation, and adaptive streaming playlists all happen automatically from a single source file.
Whether you're building a simple product demo or a full video streaming platform, the tools are all there in the browser—we just need to use them.
Top comments (0)