Docs

Cetus renders a plain HTML composition into video by seeking browser state for every frame and piping captured PNG frames into bundled ffmpeg.

Install

curl -fsSL https://cetus.cenvero.org/install | sh
curl -fsSL https://cetus.cenvero.org/install-beta | sh
curl -fsSL https://cetus.cenvero.org/install-rc | sh

Cetus ships as one binary per platform. After install it does not need Node.js, npm, system Chrome, or system ffmpeg.

Commands

cetus validate cetus.html
cetus render cetus.html -o out.mp4
cetus render cetus.html -o out.webm
cetus encode .cetus-frames -o out.mp4
cetus preview cetus.html
cetus update check
cetus update apply
cetus version

cetus.html is the recommended default filename. Cetus accepts any HTML file path.

Render commands print live progress to stderr, including asset preparation, browser launch, frame count, elapsed time, ETA, and final encoding. The final Rendered ... line stays on stdout for scripts.

FlagUse
-o, --outputRequired output path.
--fpsFrames per second. Defaults to the HTML value or 30.
--width, --heightOverride composition dimensions.
--formatmp4 or webm. Defaults from output extension.
--audioLocal audio file to mux into the final output.
--scaleScale output: 480p, 720p, 1080p, 4k, or WxH (e.g. 1920x1080). Applied at encode time, no re-render needed.
--qualityEncoder CRF value (0–51). Lower is better quality and larger file. 0 means codec default.
--resume, --frames-dirOpt-in resumable frame cache for long renders. Frames are saved as WebP and deleted after a successful render unless --keep-frames is set.
--keep-framesKeep the frame cache directory after a successful render.
--concurrencyParallel browser workers when the frame cache is enabled. Default is 1 (sequential).
--no-gpuDisable GPU acceleration for CI, Docker, or headless Linux servers.
--progress-formattext (default) or json. JSON emits newline-delimited objects for CI and build pipelines.

Output Controls

The HTML composition should normally be the source of truth for render settings. Use CLI flags only when you intentionally want to override the HTML defaults for one render.

FPS

Set the frame rate in HTML with data-fps. Override it at render time with --fps only when you want a different output than the composition declares.

<div id="root"
     data-composition-id="intro"
     data-width="1920"
     data-height="1080"
     data-duration="10"
     data-fps="30"></div>

cetus render cetus.html -o out.mp4 --fps 60
FPSBest ForTradeoff
24Cinematic motion and lighter renders.Less smooth for UI motion.
30Default web, product, launch, and social videos.Balanced render time and smoothness.
60Very smooth UI motion and fast transitions.Twice as many frames as 30 fps.
90+Special high-motion or high-refresh use cases.Large frame counts and longer render times.

Total captured frames are calculated as duration * fps. A 10 second video at 30 fps renders 300 frames. The same video at 60 fps renders 600 frames. Cetus accepts positive FPS values, so 90, 120, and 144 fps are possible when the extra render cost is acceptable.

Size

Use data-width and data-height in HTML for the composition canvas. Use --width and --height only for one-off override renders.

cetus render cetus.html -o landscape.mp4 --width 1920 --height 1080
cetus render cetus.html -o vertical.mp4 --width 1080 --height 1920
cetus render cetus.html -o square.mp4 --width 1080 --height 1080

Format

Cetus infers the format from the output extension unless --format is provided.

cetus render cetus.html -o out.mp4
cetus render cetus.html -o out.webm
cetus render cetus.html -o out.video --format mp4

Audio

Use --audio to mux a local music, voiceover, or sound-bed file into the final render. MP4 outputs use AAC audio. WebM outputs use Opus audio. Audio controls apply to the final muxed track, not browser playback.

cetus render cetus.html -o out.mp4 --audio music.mp3
cetus render cetus.html -o out.mp4 --audio music.mp3 --audio-volume 0.7 --audio-loop
cetus render cetus.html -o out.mp4 --audio music.mp3 --audio-start 2.5 --audio-fade-in 1 --audio-fade-out 2
cetus render cetus.html -o out.webm --audio voiceover.wav --audio-fade-out 1.5

Timeout and GPU

Cetus has no total render deadline by default. --timeout sets an optional maximum render time in seconds, and --timeout 0 disables that deadline. GPU stays enabled by default for WebGL, Three.js, and shader animations. Use --no-gpu on CI, Docker, or headless Linux servers where GPU drivers are not available.

cetus render cetus.html -o out.mp4 --timeout 600
cetus render cetus.html -o out.mp4 --no-gpu

Scale

Use --scale to resize the output at encode time without re-capturing frames. Useful for delivery variants from the same source render.

cetus render cetus.html -o out-480p.mp4 --scale 480p
cetus render cetus.html -o out-4k.mp4 --scale 4k
cetus render cetus.html -o out.mp4 --scale 1280x720

Quality

Use --quality to set the encoder CRF value (0–51). Lower values produce better quality at larger file sizes. The default (0) uses the codec's built-in default: CRF 23 for H.264 MP4 and CRF 30 for VP9 WebM. For high-quality archival output use 18 or lower. For smaller delivery files use 28 or higher.

cetus render cetus.html -o archive.mp4 --quality 18
cetus render cetus.html -o delivery.mp4 --quality 28

Resume and Frame Cache

Use --resume with --frames-dir for long renders where you want Cetus to save frames and reuse completed frames after a failure. If --resume is used without --frames-dir, Cetus uses .cetus-frames in the current directory. Frames are saved as lossless PNG to preserve full quality. After a successful render, the frame cache is deleted automatically unless --keep-frames is passed. When the frame cache is enabled, --concurrency controls parallel browser workers (default 1). Without these flags, frames are streamed directly to the encoder and are not kept on disk.

cetus render cetus.html -o out.mp4 --resume
cetus render cetus.html -o out.mp4 --resume --frames-dir .cetus-frames --concurrency 4
cetus render cetus.html -o out.mp4 --frames-dir renders/intro-frames
cetus render cetus.html -o out.mp4 --resume --keep-frames

Encode Subcommand

Once a frame cache exists (from --frames-dir or --resume --keep-frames), use cetus encode to build a video without re-running the browser. The frame cache directory contains a config.cetus file that records the composition metadata so the encoder has everything it needs. Multiple -o flags produce multiple output files from the same frames.

# Encode from an existing frame cache
cetus encode .cetus-frames -o out.mp4

# Multiple output resolutions from one cache
cetus encode .cetus-frames -o full.mp4 -o preview.mp4 --scale 720p

# Extract a thumbnail at a specific timestamp
cetus encode .cetus-frames --thumbnail 5s -o thumb.png
cetus encode .cetus-frames --thumbnail 01:30 -o thumb.jpg

# Override quality or fps when re-encoding
cetus encode .cetus-frames -o archive.mp4 --quality 18
cetus encode .cetus-frames -o out.mp4 --fps 24

Progress Format (JSON)

Use --progress-format json to emit newline-delimited JSON progress events to stderr instead of human-readable text. Each line is one event. Useful for CI pipelines, build servers, and custom dashboards that parse render progress programmatically.

cetus render cetus.html -o out.mp4 --progress-format json 2>progress.ndjson

# Event shapes:
# {"type":"stage","message":"Parsing composition..."}
# {"type":"frames","completed":300,"total":1800,"percent":16.67,"elapsed_ms":4200,"eta_ms":21000}

Validation

Run cetus validate before long renders to catch common composition problems without opening Chrome.

cetus validate cetus.html

The validator checks composition metadata, clip timing, missing local assets, unsupported URL schemes, remote URLs that can make renders non-deterministic, obvious inline pixel positioning outside the frame, and GSAP timelines that are not paused and registered on window.__timelines.

Composition HTML

The root element declares the canvas, duration, and identity of the video.

<div id="root"
     data-composition-id="intro"
     data-width="1920"
     data-height="1080"
     data-duration="5"
     data-fps="30">
  <h1 class="clip"
      data-start="0.5"
      data-duration="4"
      data-track-index="0">
    Hello World
  </h1>
</div>
AttributeRequiredDescription
data-composition-idYesUnique composition ID.
data-widthYesOutput width in pixels.
data-heightYesOutput height in pixels.
data-durationYesTotal duration in seconds.
data-fpsNoDefault frame rate.

Clips

Any element with class="clip" appears only during its configured time range.

<video class="clip"
       data-start="0"
       data-duration="10"
       data-track-index="1"
       data-volume="0.4"
       src="bg.mp4"
       muted playsinline></video>
  • data-start is the clip start time in seconds.
  • data-duration is how long the clip stays active.
  • data-track-index maps to z-index layering.
  • data-volume controls audio/video volume from 0.0 to 1.0.

Animation

CSS animations, Web Animations, GSAP, video, audio, WebGL, Three.js, and normal browser APIs can run inside a composition. Cetus seeks CSS and Web Animations automatically. Browser audio can drive visual timing, but final output audio should be added with --audio. GSAP timelines should be paused and registered on window.__timelines.

<script>
  window.__timelines = window.__timelines || [];
  const tl = gsap.timeline({ paused: true });
  tl.from("#title", { opacity: 0, y: 40, duration: 0.8 });
  window.__timelines.push(tl);
</script>

For canvas, WebGL, Three.js, particles, or custom JavaScript animation, draw from Cetus time instead of wall-clock time.

<script>
  window.__cetusRenderFrame = async function(time, detail) {
    // detail.frame and detail.fps are also available.
    drawSceneAt(time);
  };
</script>

Rendering Model

For each frame, Cetus calculates time = frame / fps, writes that time to document.__cetusTime and window.__cetusTime, seeks CSS/Web Animations, timelines, custom frame hooks, and media elements, waits for active images, fonts, video frames, and paint, then captures one deterministic browser frame through Chrome DevTools.

cetus.html
  -> parse data attributes
  -> launch embedded headless browser
  -> seek frame state
  -> capture PNG frame
  -> pipe frames to embedded ffmpeg
  -> write MP4 or WebM

On first render, Cetus extracts its embedded browser bundle and ffmpeg into ~/.cenvero-cetus/<version>. If the browser bundle is incomplete, Cetus repairs that cache by extracting it again.

While this runs, the terminal shows each major stage plus periodic Rendering frames: captured/total updates so a slow render is visibly moving.

Preview

The preview command serves the composition locally and injects a tiny live-reload client at runtime. It does not modify your source HTML.

cetus preview cetus.html
cetus preview cetus.html --port 8080
cetus preview cetus.html --no-open

The terminal shows the preview path, local URL, watched directory count, browser launch, and live-reload events after file changes.

Updates

For direct binary installs, Cetus can check the release manifest and apply the latest release from the installed channel.

cetus update check
cetus update apply

Stable builds update from stable, beta builds update from beta, and RC builds update from rc unless --channel is set explicitly.

The updater downloads the archive for your platform, verifies its SHA-256 from the manifest, extracts the cetus binary, and replaces the current executable.

If Cetus is managed by Homebrew, self-update is disabled and Cetus prints the Homebrew command instead.

brew update && brew upgrade cenvero-cetus