Canvas and p5 scripts
The three.js script turns a scene layer into anything three can render. That is the right tool for objects, depth and a camera. It is the wrong tool for a chart that draws itself, a counter spinning up, a hand-drawn reveal or a field of particles behind a headline — those want a flat picture, and building them from 3D meshes is how small extruded type and boxes-with-labels creep into a piece.
So the same script layer has two more runtimes. A module’s first line says which:
// @runtime canvas — plain Canvas 2D// @runtime p5 — a p5.js instance-mode sketchEither draws the layer’s box as a transparent 2D image, at the composition’s frame rate and resolution, and hands it to Mixture like any other layer: masks, filters, blend modes and grain apply, the headline above it stays a real text layer, the product shot below it stays an image. Scrubbing, stills and export render the same pixels, because the module is asked for one frame at a time from scene time — never left to run on its own clock.
Which one
Section titled “Which one”- Canvas 2D for procedural drawing: data ribbons and charts that draw on, counters and ticker digits in the project font, tape and line diagrams, hand-drawn reveals, generative texture.
- p5.js for the Processing vocabulary: noise flow fields, particle
systems, flocking, organic generative backdrops. The sketch is the
standard instance-mode shape;
frameCountandmillis()are driven from scene time, so a sketch written against p5’s own clock just works. - three.js when the beat needs objects, depth, a camera move, an instanced wall of cards, a product rig or custom GLSL.
Type, interface and product shots are never drawn inside a script. They stay real layers — a script can place or texture them (as its items), but it does not rebuild them.
Data in a script
Section titled “Data in a script”A script can read the project’s data: a CSV or JSON dataset asset, the words of a transcript, or the beats detected in an audio track. The rows go through the same filter, sort and slice pipeline as repeated layers and arrive in the module ready to draw, re-timed through an edited clip when you point them at it. That is how a chart draws itself from the real numbers, a field pulses on every beat of the music bed, or a particle spawns on each spoken word.
Ask your agent
Section titled “Ask your agent”Describe the motion and the agent picks the runtime, writes the module, creates it as a script asset and points a scene layer at it:
Behind the headline, a slow noise field of fine gold and teal trails that fades in over the first second and a half.
The revenue line should draw itself across the card over two seconds, with the dots landing as it passes and the total counting up in Switzer.
If a module fails, the agent sees the error in its own render check and
fixes it. You can also upload a .js file as an asset yourself and pick it
in the layer’s Form → Script menu; the panel shows which runtime the
file declares.
In the editor
Section titled “In the editor”Select the scene layer, set Form to Script, and choose the script
asset. The layer’s box is the script’s canvas, so size and place the layer
where the drawing should be. Parameters the module declares (a // @param
header, the same grammar as custom shader effects) appear as controls and
can be animated through the layer’s paramAnimations. Camera, lighting and
3D orientation have no effect on a flat script.
Safety
Section titled “Safety”The same sandbox as every script: no session, no storage, no network. A
canvas module imports nothing; a p5 sketch may import only p5. Loaders
(loadImage, loadFont), loop(), timers, Math.random and the wall
clock are refused when the asset is created, with the reason — they are
what would make a frame differ between preview and export. A module that
hangs is stopped and reported; it cannot take the editor with it.
Writing one
Section titled “Writing one”The full contract, both worked examples and the bake pattern for stateful
simulations live in the agent guide — the same text agents read:
get_docs(topic: "canvas"), or the canvas topic in the
agent docs. The short version:
// @runtime canvasexport default function build({ size, params, random, fonts, items }) { return { update({ ctx, time, params }) { // ctx: a CanvasRenderingContext2D, 1 unit = 1 comp px, (0,0) top-left, // cleared before every frame. Draw the whole frame for `time`. }, };}// @runtime p5export default function sketch(s, { size, fps, params, seed, items }) { s.setup = () => { s.createCanvas(size.w, size.h); }; s.draw = () => { const t = s.frameCount / fps; // scene time — set by Mixture before each frame s.clear(); // draw };}Two rules keep it a Mixture layer: draw from time (or frameCount) only,
and bake anything stateful — a particle system is stepped once, in order,
into a per-frame track that draw() reads, so any frame can be asked in
any order and land on the same pixels.