Set these in the body of POST /v1/render/screenshots or POST /v1/render/videos. The look itself (background, frame, cursor, captions) is a set of named presets: see Styles and captions.

SettingDefault
Screenshot format and quality: formatwebp (format), 90 (quality)
Pixel ratio: dpr1
Screenshot viewport: viewport1440 by 900 CSS pixels
Full page: full_pagefalse
When the page is ready: wait_untilauto (wait_until), 0 ms (wait_for_ms)
Settle: settleauto
Screenshot timeout: timeout_ms30,000 ms
Color scheme: themeauto
Overlays: overlaysremove
Screenshot auto background: auto_background.pinnone: the image is picked (per field)
Screenshot ad blocking: block_adstrue
Video formats: formatsmp4, poster
Output size and frame rate: output1920 by 1080 at 60 frames a second
Narration speed: voice.speed1
Captions: captions.burn_inkaraoke when narrated, static when silent (per field)
Camera zoom: camera.styleauto (per field)
Background under the camera: camera.background_motion0.1 (per field)
Typing shot: camera.typing_zoom2
Camera framing: camera.framingsubject
Scrolling to a step: scrollcomfortable
Cursor motion: cursor.smoothing_ms100 (per field)
Auto background: auto_background.pinnone: the image is picked (per field)
Reuse a recording: recording.reusefalse (per field)
Video viewport: viewport1440 CSS pixels wide (430 for a portrait output; about its window's width for a square or near-square one, no wider than 4:3), at the shape of the space the page gets in the frame
Start time: start_timewhen the job was created

Screenshots

Screenshot format and quality

How the capture is encoded. WebP and JPEG are smaller; PNG is lossless. quality applies to WebP and JPEG. Each entry of variants adds another encoding of the same capture and bills one more render.screenshot.

  • Field: format and quality on screenshot
  • Default: webp (format), 90 (quality)
  • Values: webp, png or jpeg (format); 1 to 100 (quality)
  • Invalid value: 400 invalid_request
  • Guide: Options
json
{"url": "https://example.com", "format": "png"}
bash
b3os screenshot https://example.com --format png

Pixel ratio

The device pixel ratio of the capture. 2 or 3 gives a sharper image with 4 or 9 times the pixels.

json
{"url": "https://example.com", "dpr": 2}
bash
b3os screenshot https://example.com --dpr 2

Screenshot viewport

The browser window's size before the capture, which sets the page's layout.

  • Field: viewport on screenshot
  • Default: 1440 by 900 CSS pixels
  • Values: width: 320 to 3,840; height: 240 to 2,160
  • Invalid value: 400 invalid_request
json
{"url": "https://example.com", "viewport": {"width": 390, "height": 844}}
bash
b3os screenshot https://example.com --width 390 --height 844

Full page

Captures the whole scroll height instead of the first screen.

json
{"url": "https://example.com", "full_page": true}
bash
b3os screenshot https://example.com --full-page

When the page is ready

wait_until: auto takes the load event, or a painted first screen when the load event is held up by a tracker or a script that never answers. load, domcontentloaded and networkidle wait for that event. wait_for_ms adds settle time after that.

  • Field: wait_until and wait_for_ms on screenshot
  • Default: auto (wait_until), 0 ms (wait_for_ms)
  • Values: auto, load, domcontentloaded or networkidle (wait_until); 0 to 10,000 ms (wait_for_ms)
  • Invalid value: 400 invalid_request
  • Guide: When the page is ready
json
{"url": "https://example.com", "wait_until": "networkidle", "wait_for_ms": 1500}
bash
b3os screenshot https://example.com --wait-for-ms 1500

Settle

auto finishes entrance animations, waits a bounded time for the first screen to fill in, and captures again while the main area is still blank. fast captures as soon as the page is ready.

json
{"url": "https://example.com", "settle": "fast"}

Screenshot timeout

How long loading the page and capturing it may take in all. A screenshot that produces no image bills nothing.

json
{"url": "https://example.com", "timeout_ms": 60000}

Color scheme

Emulates prefers-color-scheme. auto leaves the page's own default.

json
{"url": "https://example.com", "theme": "dark"}
bash
b3os screenshot https://example.com --theme dark

Overlays

remove hides consent walls and banners, sign-up and app pop-ups, dim layers and chat widgets before the capture. keep leaves them and lists them in warnings. Add hide selectors for anything it does not find.

json
{"url": "https://example.com", "overlays": "keep"}

Screenshot auto background

How the default auto background picks its library image. A small vision model picks among the images that keep the window's edges clear, and the pick is kept per site, so a site's screenshots and videos match. pin draws one image instead, repick asks the model again for the site and keeps its answer, and vision: false picks with the color rules alone. The screenshot's auto_background says which image was drawn and how it was chosen. See the auto background.

  • Field: auto_background.pin, auto_background.repick and auto_background.vision on screenshot
  • Default: none: the image is picked (auto_background.pin), false (auto_background.repick), on, where the node has the model (auto_background.vision)
  • Values: a library image in an auto pack, such as gradient-blur-collection/gradbg-049 (auto_background.pin); true or false (auto_background.repick); true or false (auto_background.vision)
  • Invalid value: 400 invalid_request
json
{"auto_background": {"repick": true}}

Screenshot ad blocking

Blocks requests to ad and tracker hosts and collapses the slots they leave empty, so no blank band is left in the image.

json
{"url": "https://example.com", "block_ads": false}

Videos

Video formats

The files a video produces besides its captions. A GIF takes longer to encode and is larger than the MP4.

json
{"formats": ["mp4", "gif", "poster"]}
bash
b3os video script.json --formats mp4,gif,poster

Output size and frame rate

The video's width, height and frame rate. When you set output, set all three; width and height must be even.

  • Field: output on video
  • Default: 1920 by 1080 at 60 frames a second
  • Values: width: 16 to 3,840; height: 16 to 2,160; fps: 24, 25, 30, 50 or 60
  • Invalid value: 400 invalid_request
json
{"output": {"width": 1080, "height": 1920, "fps": 30}}

Narration speed

How fast the narration speaks. Leave voice out for the node's default voice, kokoro-82m af_heart; "voice": null makes a silent video.

json
{"voice": {"model": "kokoro-82m", "voice": "af_heart", "speed": 1.2}}

Captions

burn_in draws captions into the frames: static as a small pill in the margin under the window, karaoke the same in step with the narration (the words spoken white, the word being spoken lit, the words to come dimmed), none for files only. sidecars are the caption files. The line limits split your say text into cues.

  • Field: captions.burn_in, captions.sidecars, captions.max_chars_per_line, captions.max_lines and captions.max_chars_per_second on video
  • Default: karaoke when narrated, static when silent (captions.burn_in), vtt (captions.sidecars), 42 (captions.max_chars_per_line), 2 (captions.max_lines), 20 (captions.max_chars_per_second)
  • Values: none, static or karaoke (captions.burn_in); any of vtt and srt (captions.sidecars); 10 to 80 (captions.max_chars_per_line); 1 to 3 (captions.max_lines); 5 to 40 (captions.max_chars_per_second)
  • Invalid value: 400 invalid_request
  • Guide: Styles and captions
json
{"captions": {"burn_in": "karaoke", "sidecars": ["vtt", "srt"]}}

Camera zoom

style auto zooms in on the element each click, select or typing step acts on, stays in through the step and while the next one starts within 2.5 s or is nearby, pans between them and ends on the whole window. The camera moves over the whole frame: the window, its shadow and the page zoom together and the background and the window's edges leave the frame, while captions stay put. It zooms so the element fits, from 1.25x up to max_zoom; a small button reaches max_zoom, and an element in a nav, aside or header bar stops at 1.25x. subtle stops every zoom at 1.25x, and off (the older auto_zoom: false) keeps the whole window in view.

  • Field: camera.style, camera.auto_zoom and camera.max_zoom on video
  • Default: auto (camera.style), none: style decides (camera.auto_zoom), 1.7 (camera.max_zoom)
  • Values: auto, subtle or off (camera.style); true or false (camera.auto_zoom); 1 to 3 (camera.max_zoom)
  • Invalid value: 400 invalid_request
json
{"camera": {"style": "subtle"}}

Background under the camera

As the camera zooms, the background moves and zooms by background_motion of the window's move, a tenth by default: 0 holds it still, 1 zooms it with the window so the whole frame zooms as one. background_blur softens it: an image background is drawn 4 px soft on the whole window at 1080p and softer as the camera zooms in, about 7 px at 1.6x on a 1080p video; left out, only an image background softens.

  • Field: camera.background_motion and camera.background_blur on video
  • Default: 0.1 (camera.background_motion), on for an image background (the default look's among them), off otherwise (camera.background_blur)
  • Values: 0 to 1 (camera.background_motion); true or false (camera.background_blur)
  • Invalid value: 400 invalid_request
json
{"camera": {"background_motion": 1, "background_blur": false}}

Typing shot

A field typed into, by a type step or a click that a type without a target follows, is one shot: the camera zooms to typing_zoom, framed from the start of the field, and holds there through the last key. It never zooms past max_zoom, nor past the zoom where the page is as sharp as captured, so the default types at 1.73x on the default canvas; style: subtle keeps its 1.25x.

json
{"camera": {"typing_zoom": 1.6}}

Camera framing

subject frames the panel the element sits in, not the click point: the nearest enclosing element with a box of its own or a section, form, table, list or dialog, at least 240 x 120 CSS px, fitted with 24 px around it into the frame above the captions, from 1.4x up to max_zoom, and placed from its start when it is wider than the view; a view edge moves off a neighbouring panel to the gap beside it. A hover the narration speaks over, an element a wait waits for, and after a click that loads a page the element its beat waits for, are framed and held through their line. point frames the click point as before.

json
{"camera": {"framing": "point"}}

Scrolling to a step

comfortable scrolls an element outside 8% to 85% of the viewport's height so its panel sits in the middle, before the cursor sets off for it and as a waited-for element appears; one already there never scrolls the page. minimal scrolls only an element outside the viewport, and off only by your own scroll steps, failing a step whose element is outside the viewport.

json
{"scroll": "minimal"}

Cursor motion

Each cursor move starts softly and settles over about smoothing_ms, as Screen Studio's smoothed cursor does, and still arrives when it would have; 0 keeps the eased path. After a click, a select or the first key typed into a field, move_aside moves the cursor just below the element, so it does not cover the label or the text being typed while the camera holds. Still for hide_after_ms, it fades out, and back in before its next move; 0 never hides it. A restyle keeps the path its video was recorded with.

  • Field: cursor.smoothing_ms, cursor.hide_after_ms and cursor.move_aside on video
  • Default: 100 (cursor.smoothing_ms), 2,000 (cursor.hide_after_ms), true (cursor.move_aside)
  • Values: 0 to 300 (cursor.smoothing_ms); 0 to 10,000 (cursor.hide_after_ms); true or false (cursor.move_aside)
  • Invalid value: 400 invalid_request
json
{"cursor": {"hide_after_ms": 0, "move_aside": false}}

Auto background

How the default auto background picks its library image. A small vision model picks among the images that keep the window's edges clear, and the pick is kept per site, so a site's videos and screenshots match. pin draws one image instead, repick asks the model again for the site and keeps its answer, and vision: false picks with the color rules alone. result.auto_background says which image was drawn and how it was chosen. See the auto background.

  • Field: auto_background.pin, auto_background.repick and auto_background.vision on video
  • Default: none: the image is picked (auto_background.pin), false (auto_background.repick), on, where the node has the model (auto_background.vision)
  • Values: a library image in an auto pack, such as gradient-blur-collection/gradbg-049 (auto_background.pin); true or false (auto_background.repick); true or false (auto_background.vision)
  • Invalid value: 400 invalid_request
json
{"auto_background": {"repick": true}}

Reuse a recording

reuse replays your project's newest recording of the same source, steps, viewport, seed and start time, up to max_age_s old, instead of loading the site again. Faster, and the page shows what the site served then. result.recording says which happened.

  • Field: recording.reuse and recording.max_age_s on video
  • Default: false (recording.reuse), 86,400 s (recording.max_age_s)
  • Values: true or false (recording.reuse); 60 to 86,400 s (recording.max_age_s)
  • Invalid value: 400 invalid_request
json
{"recording": {"reuse": true, "max_age_s": 3600}}

Video viewport

The browser's size in CSS pixels during the recording. Left out, the window fills the frame with even margins.

  • Field: viewport on video
  • Default: 1440 CSS pixels wide (430 for a portrait output; about its window's width for a square or near-square one, no wider than 4:3), at the shape of the space the page gets in the frame
  • Values: width: 320 to 3,840; height: 240 to 2,160
  • Invalid value: 400 invalid_request
json
{"viewport": {"width": 1280, "height": 800}}

Start time

The page's Date when the video starts; the clock then moves with the video. The finished video's result.start_time gives the value used: send it back with the same seed (randomness) and recording.reuse and a re-render is identical. Without recording.reuse the site loads again, so a page that changed shows the change.

  • Field: start_time on video
  • Default: when the job was created
  • Values: an RFC 3339 date and time
  • Invalid value: 400 invalid_request
json
{"seed": 7, "start_time": "2026-11-01T09:00:00Z"}

Fixed in the beta

These work one way in the beta, and no request field changes them.

Video encoding

H.264, BT.709. MP4 files are H.264 from x264 at the veryfast preset and CRF 20, with BT.709 color. No field changes the codec or quality.

Video capture scale

Picked per segment. Parts of a video where the camera zooms in are captured at device scale 2, so zoomed text stays sharp, and the rest at 1. Screenshots take dpr; videos have no such field.

Video background

Picked from 24 library images. A video's auto background, the default, is one of the library images GET /v1/render/backgrounds?tag=video lists, picked from the video's first frame for the page's tone and accent color, at least 12 L* from the page at the window's edges. The timeline's style names it. studio names the soft mesh videos drew before; background_ref names any library background. The set is the node's; no field changes it.

Render egress

Through the node's egress guard. Every capture browser's only way out is the node's egress proxy, under the same egress guard as vision, from the node's own address. You cannot bring a proxy.

Signed-in captures

Through a vision profile. A screenshot with profile, or a video with source.profile, loads that vision profile's cookies and storage before the page loads: into a video's probe and every capture segment. It needs vision:profiles:use. A page that is still signed out fails and bills nothing.

Signed URL lifetime and retention

24 hours and 7 days. A signed artifact URL works for 24 hours, and never past its file. Screenshots, videos and their files are kept 7 days. Fetch the resource again for a fresh URL.

Ask a question... ⌘I