Skip to content

Pages and players ​

index.html is an ordinary page. Each render host on it names a scene file, and Studio renders that scene for the reader:

html
<marimo-output render="scene.py">
  <video controls playsinline preload="metadata"></video>
</marimo-output>
<marimo-cell name="controls"></marimo-cell>

<marimo-cell name="controls"> places the notebook cell called controls with its live widgets. Moving a slider there runs the notebook, and every scene that reads an affected value renders again. Studio's projections reference describes cell and output hosts.

Several scenes on one page ​

Add a scene file and a host for it. The Fourier example plays the vector animation and a spectrum on one page:

html
<marimo-output render="scene.py" aria-label="Rotating vectors">...</marimo-output>
<marimo-output render="spectrum.py" aria-label="Spectrum">...</marimo-output>

Each scene renders on its own, from the results it reads. A host for a missing scene file reports manim-scene-missing at the host's line.

What a render publishes ​

FileContents
animation.mp4The movie, H.264 with faststart for streaming
poster.pngThe last frame, shown before playback
metadata.jsonduration, fps, width, and height

Studio serves the movie with byte ranges, so the player seeks while it streams. The host's toolbar holds a Download link for the movie.

The player ​

Markup inside a host replaces Studio's stock video player, and the page fills it from the host's render record. The starter's player.js drives every host on the page:

js
const url = (record, path) => record.files.find((file) => file.path === path)?.url;

for (const host of document.querySelectorAll("marimo-output[render]")) {
  const video = host.querySelector("video");
  const show = (record) => {
    video.poster = url(record, record.preview) ?? "";
    video.src = url(record, record.main);
  };
  host.addEventListener("marimo-output-ready", (event) => show(event.detail));
  host.addEventListener("marimo-output-updated", (event) => show(event.detail));
  if (host.marimoRender) show(host.marimoRender);
}

The starter's full player also keeps playing when a new movie arrives during playback, and adds replay, looping, and playback speed. Remove the markup inside a host to use Studio's stock player.

Render states ​

A host keeps its last movie on screen while the next one renders.

data-stateThe host
loadingWaits for its first movie
staleShows the previous movie while a new one renders
readyShows the movie for the current notebook results
errorShows the last movie with the error and its scene line

Style the states from page CSS. The starter dims a stale movie:

css
marimo-output[data-state="stale"] video {
  opacity: 0.6;
  transition: opacity 0.2s;
}

host.marimoRender.absent lists reads that rendered at their defaults, and the toolbar notes them as Showing the default for <target>.

Quality ​

Set quality in view.toml:

toml
schema = 1
provider = "manim-marimo/manim"

[options]
quality = "draft"
QualityResolutionFrames per second
draft640 × 36015
preview960 × 54024
production1920 × 108030

Without quality, Studio Preview renders at preview and run mode and export render at production.

Page assets ​

Put images and fonts beneath assets/ and reference them from index.html. The build publishes the page, its CSS and JavaScript, and assets/. Scenes and their helper modules stay private to the render.