# How sketches run (https://docs.fuser.studio/docs/creative-code/runtime)

Sketches in Fuser follow a small contract: you define `setup`, `draw`, and optionally `onEvent`, and Fuser handles the rest. This page covers that contract, what's in scope inside your code, and the things that won't work even though they would in plain browser JavaScript.

## Supported libraries [#supported-libraries]

Pick a library from the **library selector** in the node's settings bar. Three are live, each suited to a different kind of sketch.

    The raw 2D drawing API — no abstraction, lowest overhead.

    * **In scope** — the `ctx` 2D context, the standard `CanvasRenderingContext2D` API.
    * **Best for** — full control, porting tiny existing sketches, generative work that doesn't need helpers.
    * **Drawback** — you write your own helpers for things p5 gives you for free (random color, noise, easings).

    The creative-coding standard.

    * **In scope** — `setup`, `draw`, `mouseX`, `mouseY`, `noise()`, all p5 globals.
    * **Best for** — most 2D sketches. The default if you're not sure.
    * **Drawback** — slightly heavier than raw canvas; abstracts the 2D context behind p5's vocabulary.

    WebGL-based 3D rendering.

    * **In scope** — the `THREE` namespace, scenes, cameras, meshes, lights, materials, shaders.
    * **Best for** — anything with depth, lighting, or shaders.
    * **Drawback** — WebGL contexts are limited (see below); you can only have so many Three.js sketches alive at once.

The model selector in [prompt mode](https://docs.fuser.studio/docs/creative-code/prompting.md) generates code matching whichever library you've picked.

## Lifecycle: `setup`, `draw`, `onEvent` [#lifecycle-setup-draw-onevent]

Every sketch follows the same shape. Think of it as: &#x2A;*set up your state once, then redraw it every frame, optionally responding to events.**

```javascript
// Variables you want to keep across frames live at the top.
let particles = [];
let bgColor;

function setup() {
  // Runs once when the sketch starts.
  // Initialize anything you want to keep across frames here:
  // load assets, seed your particle array, choose colors.
  bgColor = 'white';
  for (let i = 0; i < 200; i++) {
    particles.push({ x: width / 2, y: height / 2, vx: 0, vy: 0 });
  }
}

function draw() {
  // Runs every frame at the canvas frame rate.
  // Re-render the current state — DON'T allocate new arrays here,
  // mutate the ones from setup. The canvas isn't auto-cleared.
  background(bgColor);
  for (const p of particles) {
    p.x += p.vx;
    p.y += p.vy;
    circle(p.x, p.y, 4);
  }
}

function onEvent(event) {
  // Optional. Mouse and resize events arrive here.
  // Use it to mutate state that draw() will pick up next frame.
  if (event.type === 'mousedown') {
    bgColor = bgColor === 'white' ? 'black' : 'white';
  }
}
```

`setup` is awaited — mark it `async` if you need to load anything before the first frame. `draw` is called repeatedly at the canvas frame rate; keep it cheap and avoid allocating per frame. `onEvent` is optional — define it only when you want to react to user input beyond the auto-injected `mouseX` / `mouseY` / `isMousePressed` globals.

**State lives at module scope** (the variables declared above `setup`). `setup` initializes them; `draw` mutates them; `onEvent` reacts. Don't try to share state via the DOM or storage — neither is available (see [What's blocked](#whats-blocked)).

## What's in scope [#whats-in-scope]

Inside your sketch, these names are available:

| Name                                  | What it is                                                                                                               |
| ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| **`width`*&#x2A; / &#x2A;*`height`**  | The canvas dimensions in pixels.                                                                                         |
| **`mouseX`*&#x2A; / &#x2A;*`mouseY`** | The mouse position, in pixel coordinates.                                                                                |
| **`isMousePressed`**                  | `true` while a mouse button is held.                                                                                     |
| **`params`**                          | The parsed `@fuser-params` values — see [Parameters and inputs](https://docs.fuser.studio/docs/creative-code/parameters-and-attachments.md).         |
| **`attachments`**                     | Connected attachment data — bitmaps, video frames, audio buffers, meshes — keyed by socket name.                         |
| **`getDeltaTime()`**                  | A function returning seconds since the previous frame. Useful for time-based animation.                                  |
| **`canvas`**                          | The underlying drawing surface. You rarely need to touch it directly.                                                    |
| **`ctx`*&#x2A; / &#x2A;*`gl`**        | The 2D or WebGL context, depending on the library.                                                                       |
| Library globals                       | `p5` in p5 mode, `THREE` in Three.js mode, plus any [supplemental libraries](#supplemental-libraries) you've opted into. |

`mouseX`, `mouseY`, and `isMousePressed` are auto-injected — you don't need to import or declare them.

## What's blocked [#whats-blocked]

Some browser APIs you might reach for in regular JavaScript don't work inside a sketch. Each restriction has a Fuser-native replacement — the table below pairs them up.

| Don't reach for…                                                        | Do this instead                                                                                                                              |
| ----------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `document`, `window`, `requestAnimationFrame` (the **DOM**)             | Use `draw` for animation; use `width` / `height` / `mouseX` / `mouseY` for input.                                                            |
| Browser storage — cookies, `localStorage`, `sessionStorage`             | Persist values as `@fuser-params` defaults (auto-saved when the user tweaks a slider), or save the canvas as an asset via the Save button.   |
| Arbitrary `fetch()` calls                                               | Pipe the data into an [**attachment socket**](https://docs.fuser.studio/docs/creative-code/parameters-and-attachments.md#attachment-sockets) from another Fuser node.    |
| Dynamically-generated code — `eval`, `new Function`, dynamic `import()` | Add the library you need to the [`@fuser-libs` block](#supplemental-libraries) (currently only `ml5`), or restructure to avoid runtime eval. |
| Spawning workers (`new Worker`, `SharedWorker`)                         | Lighten the work in `draw` — see [adaptive frame throttling](#frame-throttling) — or split the work across multiple sketches.                |

If your sketch fails because it touched one of the blocked APIs, Fuser flags it before run with a line/column pointer in the editor — see [Errors before your sketch runs](#errors-before-your-sketch-runs).

## Errors before your sketch runs [#errors-before-your-sketch-runs]

Fuser checks your code before running it. If you've used a blocked API or made certain other errors, you'll see a message in the editor with the line and column of the problem.

Fix the offending line and the sketch runs. Checks happen on every code change, so you'll see issues immediately rather than at first execution.

## WebGL context limits [#webgl-context-limits]

Three.js sketches use a WebGL context. Browsers cap how many WebGL contexts can exist at once — usually 8 to 16. When you exceed the cap, Fuser will show a warning and stop the sketch. Close one of the existing WebGL sketches and the new one picks up.

A single sketch generally won't hit this on its own; the issue surfaces when you accumulate many WebGL sketches in the same flow.

## Frame throttling [#frame-throttling]

Sketches don't always run at 60 fps. Fuser adapts the frame rate to keep the page responsive when many sketches are alive at once or a single sketch is slow:

* A sketch whose `draw()` is fast targets **30 fps**.
* A sketch that's slower drops to **15 fps**.
* Background or off-canvas sketches drop further to **5 fps**.

The [FPS readout](#the-fps-readout) on the canvas shows the actual frame rate so you can see throttling in action. If the readout is consistently low while your `draw()` is meant to be cheap, look for accidental work — large allocations per frame, image filters, or layout recalculations.

## Transient error tolerance [#transient-error-tolerance]

A single thrown error inside `draw()` doesn't kill your sketch. Fuser tolerates **up to three consecutive frame errors** before stopping the sketch and surfacing the error. This means an intermittent failure (a missing attachment for one frame, a flaky external library) gets a chance to recover without forcing you to re-run.

If you're hitting the three-error limit repeatedly, the **Try to fix** action in the editor sends the runtime error back to the model for a targeted code repair — see [Editor](https://docs.fuser.studio/docs/creative-code/editor.md).

## Supplemental libraries [#supplemental-libraries]

Beyond the three primary libraries, Fuser supports opt-in **supplemental libraries** loaded on demand. Currently the only one is **ml5.js** — a friendly wrapper around common machine-learning models for things like image classification, body pose detection, and hand tracking.

Opt in with a `// @fuser-libs` block at the top of your sketch — one `// @lib <name>` line per library, closed by `// @end-fuser-libs`:

```javascript
// @fuser-libs
// @lib ml5
// @end-fuser-libs

let classifier;

async function setup() {
  classifier = await ml5.imageClassifier('MobileNet');
}
```

When the block is present, the listed libraries are loaded and their globals (here, `ml5`) become available in your sketch. Without the block, ml5 isn't included — leave it off when you don't need it.

> [!WARNING] The single-line form is silently ignored
>
> The block must use the three-line `// @fuser-libs` / `// @lib …` / `// @end-fuser-libs` shape. A single-line `// @fuser-libs: ml5` does not parse and the library will not load — your sketch will then crash with `ReferenceError: ml5 is not defined` at runtime.

## The FPS readout [#the-fps-readout]

A small live indicator on the canvas shows the current frame rate. **Yellow** when you drop below 30 fps; **red** when you drop below 15. Use it to spot expensive draw loops — if the readout goes red, you're either doing too much per frame or running too many sketches at once.

## What's Next? [#whats-next]

  - [Parameters and inputs](https://docs.fuser.studio/docs/creative-code/parameters-and-attachments.md): Where `params` and `attachments` come from before they reach your sketch.

  - [Saving and sharing](https://docs.fuser.studio/docs/creative-code/publishing-and-export.md): Capture the running canvas, save versions, and publish public embeds.