Use the browser’s video element and a canvas. Wait for loadedmetadata, seek with currentTime when you need an exact timestamp, wait for seeked, draw the frame at the video’s intrinsic dimensions, and export the canvas as a JPEG Blob. The method works for same-origin media and for cross-origin media whose server grants CORS permission.
What the conversion pipeline does
An HTML <video> element exposes the frame at its current playback position. A canvas can copy that rendered frame with drawImage(), after which the canvas can encode it as JPEG using toBlob('image/jpeg', quality) or toDataURL('image/jpeg', quality).
- Load enough metadata to know the duration and intrinsic dimensions.
- Pause or seek the video to the desired position.
- Wait until the seek has completed.
- Set the canvas to
videoWidthbyvideoHeight. - Draw the video frame and encode it as a JPEG.
Use intrinsic dimensions rather than the element’s CSS size. A 320-pixel-wide video displayed at 1,280 pixels would otherwise produce a low-resolution or incorrectly scaled capture.
Complete browser implementation
This example saves the frame at 12.5 seconds. Pass no time, or pass undefined, to capture the currently displayed frame.
#1 Best Overall
- 4K Pass Through & Full HD Recording: Experience 4K content on TV while capturing in full HD with EzRecorder 330. Compatible with PS5, Nintendo Famicom, and set-top boxes (recording of encrypted or HDCP content is not supported). Ideal for digitizing VHS tapes.
- High Efficiency Video Coding (HEVC - H.265): Enjoy 2x compression rate with H.265 encoding. Maintains exceptional video quality while saving storage space.
- Remote Operation and Included IR Blaster: Control EzRecorder 330 with ER330G remote (batteries not included). Built-in IR blaster for set-top box activation.
- Flexible Storage Solutions: Record content using external hard drives, micro SD cards, or NAS storage. Choose the storage device that suits your needs effortlessly. Note: SD card not included.
- Stream Directly Without a PC: Connect gaming consoles, camcorders, or DSLRs directly to EzRecorder 330. Effortlessly broadcast to YouTube, Facebook, or Twitch without complex setups.
<video id="source"
src="video.mp4"
crossorigin="anonymous"
preload="metadata"
controls></video>
<button id="save">Save JPG</button>
<script>
const video = document.querySelector('#source');
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
function waitForEvent(target, name) {
return new Promise((resolve, reject) => {
const done = () => {
target.removeEventListener(name, done);
target.removeEventListener('error', fail);
resolve();
};
const fail = () => {
target.removeEventListener(name, done);
target.removeEventListener('error', fail);
reject(target.error || new Error(`Video ${name} failed`));
};
target.addEventListener(name, done, { once: true });
target.addEventListener('error', fail, { once: true });
});
}
async function frameAsJpeg(time, quality = 0.92) {
if (video.readyState < 1) {
await waitForEvent(video, 'loadedmetadata');
}
if (Number.isFinite(time)) {
const maximum = Number.isFinite(video.duration)
? video.duration
: time;
video.currentTime = Math.max(0, Math.min(time, maximum));
await waitForEvent(video, 'seeked');
}
if (!video.videoWidth || !video.videoHeight) {
throw new Error('Video dimensions unavailable');
}
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
return new Promise((resolve, reject) => {
canvas.toBlob(blob => {
if (blob) resolve(blob);
else reject(new Error('JPEG encoding failed'));
}, 'image/jpeg', quality);
});
}
document.querySelector('#save').onclick = async () => {
try {
const blob = await frameAsJpeg(12.5, 0.92);
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'frame-12.5s.jpg';
link.click();
URL.revokeObjectURL(url);
} catch (error) {
console.error(error);
alert(error.message);
}
};
</script>
The crossorigin attribute must be present before src is assigned. The media server must return an Access-Control-Allow-Origin response that permits your page. If it does not, the video may render normally but the canvas cannot be read back.
Capture the current frame
For a paused video, the current playback position represents the displayed frame. Call frameAsJpeg(undefined) after pausing. For a playing video, pause first if you need a stable image; otherwise the frame can change between your call and the draw.
Choose JPEG quality and output size
The quality argument is normally between 0 and 1. Values around 0.85 to 0.95 are a practical starting point. Lower values reduce file size and introduce more compression. The output pixel dimensions remain those assigned to the canvas, so resize deliberately if you need thumbnails rather than full-resolution frames.
Extract frames at exact timestamps
Set video.currentTime to the requested number of seconds and wait for seeked before drawing. Seeking is asynchronous: drawing immediately can capture the previous frame, an intermediate buffering state, or a frame from the old position.
Rank #2
- 【Real USB3.0 YUY2 1080P 60FPS Capture Card】It is a real USB3.0 HDMI capture card with real USB3.0 protocol chipset, fast transfer rate and low power consumption. It can support capture YUY2 1080P 60FPS resolution,share superb game skills with excellent visual experience.
- 【Support 4K@30Hz Input and Pass-through】This capture card supports 4K@30Hz input and 4K@30Hz Zero Latency Pass-through.The capture resolution of this game capture card can reach up to 1080p 60fps; Note: USB3.0 output can only reach up to 1080P 60FPS, HDMI OUT output can reach 4K 30FPS.The capture card for streaming is suitable for high definition acquisition, live broadcasts, video recording, conference rooms etc.
- 【Support Mic-in for Commentary】This audio video capture card allows you to plug in a 3.5mm headset or microphone so you can easily stream your voice and record your voice through the port. For example, add external audio to a live game or interview. Note: it does not record party chats.
- 【Plug & Play】Once plugged in, the capture card is identified automatically. No drivers or external power supply required.Detects inputs and automatically adjusts outputs. you can use third party software to capture or stream your HD gaming video sources. Capture cards for streaming are very effective for recording game play.
- 【Compatible with Multiple Systems】This 4K capture card suitable for Windows Unix,Mac OS,Windows 7/8/10/11, Android and more. It is compatible with most devices such as Wii U, PS3, PS4, PS5, Xbox One, Xbox 360, Wii, Switch, DVDs, DSLR Camera, ZOSI Security Camera and set top box etc. Make it easier to provide live streaming for Twitch, Youtube, OBS, Facebook live, skype, XSplit, Tiktok, Potplayer and VLC.
async function saveAt(video, seconds, quality = 0.9) {
const blob = await frameAsJpeg(seconds, quality);
const url = URL.createObjectURL(blob);
const a = Object.assign(document.createElement('a'), {
href: url,
download: `frame-${seconds.toFixed(3)}s.jpg`
});
a.click();
setTimeout(() => URL.revokeObjectURL(url), 0);
}
// Example: save frames at three positions.
for (const seconds of [0, 5, 12.5]) {
await saveAt(video, seconds);
}
Do not assume that a decimal timestamp identifies an independently encoded picture at exactly that instant. The browser seeks to the nearest decodable point and presents the resulting frame. For live streams, duration and random access may be unavailable; capture the currently rendered frame and label it as such in your interface.
JPEG Blob versus data URL
| Output | Use it when | Trade-off |
|---|---|---|
toBlob() |
Downloading, uploading, storing, or processing an image | Asynchronous and memory-efficient for large images |
toDataURL() |
A small inline image or an API that explicitly requires a data URL | Creates a base64 string that is larger and can consume substantial memory |
const dataUrl = canvas.toDataURL('image/jpeg', 0.92);
// Example: document.querySelector('#preview').src = dataUrl;
For repeated or high-resolution captures, prefer toBlob(). Revoke object URLs after downloads or previews are finished.
Cross-origin video and the tainted-canvas error
A canvas becomes “tainted” when it contains pixels from another origin without the required CORS authorization. Readback operations—including toBlob(), toDataURL(), getImageData(), and canvas captureStream()—then throw a SecurityError.
Correct configuration
- Set
crossorigin="anonymous"on the video before assigning its source. - Configure the media host to return an appropriate
Access-Control-Allow-Originheader for your page’s origin (or*where that policy is acceptable). - Reload the media after changing either setting; an already-loaded resource does not gain CORS permission retroactively.
Client-side JavaScript cannot override a server that withholds permission. If you control neither origin, download and process the media on a server that is authorized to access it, or use a source that provides CORS headers.
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Rank #3
Repeated capture and stream-based alternatives
Capture the rendered video stream
HTMLMediaElement.captureStream() produces a real-time MediaStream containing the rendered video track. This is useful when another Web API, recorder, or processing pipeline needs continuous frames rather than individual JPEG files.
const stream = video.captureStream();
const [track] = stream.getVideoTracks();
console.log(track.readyState, track.getSettings());
Browser support and behavior vary, and capture still depends on origin-clean media. The stream carries video frames; it does not automatically encode each one as JPEG.
Request individual canvas frames
For a canvas pipeline, canvas.captureStream(0) requests a stream with a zero automatic frame rate. Where supported, call requestFrame() on the video track after each draw to control when a frame enters the stream.
const stream = canvas.captureStream(0);
const track = stream.getVideoTracks()[0];
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
if (typeof track.requestFrame === 'function') track.requestFrame();
This is a stream workflow, not a replacement for JPEG encoding. You still need a recorder or another consumer, and an origin-unclean canvas cannot be captured.
Rank #4
- RECORD AND CONVERT: A capture card that has the ability to record videos or audio files. Records full HD 1080p video quality with an HDMI pass through support. After recording, it also converts the file into digital format
- STREAM LIVE: This video game capture device has the ability to instantly stream live on social media and YouTube while playing. It can play and record simultaneously for a hassle free media file creation
- HOT BUTTON RECORDING: Manage recorded media easily and post to social media like YouTube. It has an on-board hot button for one-click recording and screenshot capture ability
- PLUG AND PLAY: No installation needed, simply plug and play this device to start recording, It also has a built-in software that supports voice commentary and multi language support
- COMPATIBLE DEVICES: This capture card can record videos from any devices with HDMI output like Xbox 360, Xbox One, PS4, PS3, Wii U, PC computers, PS3, PS4, Blu-ray DVD player, Set-top Box, and DVR
ImageCapture when a MediaStreamTrack already exists
If you already have a MediaStreamTrack, ImageCapture.grabFrame() returns an ImageBitmap that you can draw to a canvas. takePhoto() instead returns an encoded Blob. These APIs are optional browser paths and are most useful for camera or stream tracks, not for a simple embedded-video download button.
Common failures and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
videoWidth and videoHeight are zero |
Metadata has not loaded, or the media failed | Wait for loadedmetadata; inspect the video’s error event and source URL. |
| Wrong frame after seeking | Drawing happened before seeking completed | Set currentTime, then await seeked before drawImage(). |
SecurityError: Tainted canvases may not be exported |
Cross-origin media lacks CORS permission | Set crossorigin before src and configure the media server’s CORS response. |
JPEG Blob is null |
Encoding failed or the canvas is invalid | Check canvas dimensions, origin cleanliness, and browser support; reject a null callback result. |
| Seeking never finishes | Live media, an unsupported range request, or a stalled load | Capture the current frame instead, verify network delivery, and expose the limitation to users. |
| Output is blurry | Canvas sized from CSS dimensions instead of intrinsic video dimensions | Assign canvas.width = video.videoWidth and canvas.height = video.videoHeight before drawing. |
| Downloads stop working after many captures | Object URLs are not released | Call URL.revokeObjectURL() after each download or preview is finished. |
Performance, reliability, and privacy considerations
- Decode cost: Seeking repeatedly through a long, highly compressed video can be slow because the browser may decode from a nearby keyframe.
- Memory: Full-resolution canvases and base64 data URLs are expensive. Process one frame at a time and use Blobs for transfers.
- Concurrency: Queue captures rather than changing
currentTimefrom several asynchronous operations at once; each seek can invalidate the previous request. - Playback state: Preserve whether the video was playing before a capture if your UI should resume playback afterward.
- Security: Treat captured frames as user data. Avoid inserting untrusted data URLs into contexts that interpret markup, and revoke temporary object URLs.
- Browser maturity: The DOM capture specifications are Working Drafts, so test stream-based features in every browser you support. The basic canvas-to-JPEG path is simpler and more broadly dependable.
Or skip the browser setup
For a server-side screenshot of a page that embeds the video, ScreenshotNeo accepts one GET request and returns PNG, JPEG, WebP, or PDF. It is useful when you need the rendered page rather than a timestamped video frame: cookie banners, newsletter popups, and chat widgets are removed before the shot; bot checks, blank pages, timeouts, failed loads, and cache hits are not billed; and an MCP server lets AI agents take screenshots.
Use the API documentation at https://screenshotneo.com/docs/ for the full option set, including waits, custom JavaScript, selectors, device presets, and PDF settings.
cURL
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com/video-page -o shot.webp
Python
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://example.com/video-page"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
Node.js
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://example.com/video-page'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
require('fs').writeFileSync('shot.webp', Buffer.from(await res.arrayBuffer()));
ScreenshotNeo includes 1,000 screenshots per month free with no card. Paid plans start at $5 for 3,000 screenshots, and every feature is available on every plan. Create a free ScreenshotNeo account.
Free tools Windows power users keep installed
One-click scans. No signup required.
FAQ
Can I convert a video frame without playing the video?
Yes. Metadata loading and seeking do not require visible playback. A browser must still be able to decode the media and access it under its CORS policy.
Best Value
- 【8MP HD Camera】Smart glasses with camera feature an 8MP HD camera for clear, stable shooting. Enjoy up to 12 minutes of video recording and 120 minutes of audio recording, plus intelligent AI object recognition. With easy WiFi transfer, you can quickly import all your photos, videos and audio clips—making it effortless to capture life’s moments hands-free
- 【Immersive Wireless Audio】Ai glasses with camera equipped with advanced Bluetooth 5.4 and dual-microphone ENC noise cancellation, these smart glasses meet all your audio needs. Take calls, listen to music or audiobooks, and access AI functions (audio/video recording, voice commands) with ease. Hi-Fi sound delivers clear vocals and great bass, ensuring a top-tier audio experience in any scenario
- 【AI Assistant & Translation】Ai glasses built-in AI assistant and translator make communication and work more efficient. Support real-time translation for international talks, simultaneous interpretation, and auto meeting notes—perfect for breaking language barriers during global conversations or multi-language meetings, helping you work smarter
- 【Long Battery Life】Ai glasses with camera come with fast charging (full charge in 2 hours) and reliable battery life for daily use. Enjoy 7 hours of non-stop music and 4 hours of smooth calls, making them your ideal companion for commuting, working or relaxing all day long
- 【Upgraded Materials】Smart glasses crafted with lightweight, flexible TR90 frames and bendable temple legs for a comfortable, customizable fit. IP68 waterproof and dustproof (resistant to rain, splashes and dust) ensures durability, even during hiking or sudden downpours—built to last in any environment
Why does the saved JPG have a black or transparent background?
JPEG has no transparency. Transparent canvas pixels are typically composited against black during JPEG encoding; draw an explicit background first if your source or canvas includes transparency.
Can this method extract subtitles or controls?
It captures video pixels, not separate subtitle text or browser controls. Rendered captions may be included depending on how the browser composites them, but controls are outside the video image.
Frequently Asked Questions
Can I convert a video frame without playing the video?
Yes. Metadata loading and seeking do not require visible playback, provided the browser can decode the media and access it under its CORS policy.
Recommended Free Tools
Why does the saved JPG have a black or transparent background?
JPEG has no transparency. Transparent canvas pixels are typically composited against black during encoding; draw an explicit background first when needed.
Can this method extract subtitles or controls?
It captures video pixels, not separate subtitle text or browser controls. Captions and controls are handled separately by the browser.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

