---
contentHash: sha256:85bfe03b1ecdb423c0290cd3439868c397db1b017545ac59cbaa47a7b59110d5
documentId: guide:for-agents
kind: guide
schemaVersion: 1
skenraVersion: 0.1.0-alpha.1
---
# Skenra for agents

<a id="overview"></a>

## Overview

Skenra is a WebGPU 3D framework authored as HTML: `<sk-scene>` and its children ARE the scene graph. Write declarative markup first; script is the escape hatch, not the default. Read the reflex table before writing your first scene — it catches the mistakes agents make by instinct.

<a id="workflow"></a>

## Workflow

Same operation chain on every surface:

- Local: `search → doc → author .sk → validate → render`
- MCP: `search → doc → author .sk → validate → create_scene`

Search topics, fetch scoped documents, author `<sk-scene>` markup, validate, then render. Never guess an element or attribute — `doc` it first.

<a id="decision-tree"></a>

## Declarative-first decision tree

| You are building | Reach for | Example |
|---|---|---|
| Shapes with colors/materials | primitives + scene-root materials bound via paint children | [primitives-materials](for-agents-example-primitives-materials.md) |
| Property motion over time | `<sk-animation>` + `<sk-keyframe>` inside the owning element | [keyframes-easing](for-agents-example-keyframes-easing.md) |
| Frame/pose the view | scene-root `<sk-camera-orbit-control fit-targets="auto">` + a camera | [camera-fit](for-agents-example-camera-fit.md) |
| Motion derived from another node | constraints (orbit, target-at, follow, align-to-spline) | [constraints](for-agents-example-constraints.md) |
| Many copies of a shape | `<sk-cloner>` + a points-source child | [cloners](for-agents-example-cloners.md) |
| Spatially varying effects/masking | by-id field resources referenced from constraints/tonemaps | [fields](for-agents-example-fields.md) |
| Labels, callouts, HUD | annotations (live/overlay/rasterized) + spline connectors | [annotations](for-agents-example-annotations.md) |
| Interaction / custom logic | lowercase `on*` handler attributes; `<script>` with standard DOM | [scripting](for-agents-example-scripting.md) |
| 2D-only UI (spinner, progress) | do NOT use Skenra — CSS/SVG/Canvas2D are the right tools | — |

<a id="escape-hatch"></a>

## Script escape hatches

Everything in the tree above is declarative today — including spatial fields (noise, random, voronoi), composite fields, radial-push point constraints, and dragging via the `draggable` attribute. Script is the right tool ONLY for:

| Scenario | Status | Write |
|---|---|---|
| Multi-touch gestures (pinch, swipe, rotate) | intentionally imperative | compose from pointer handlers (`onpointerdown`/`onpointermove`) |
| Multi-branch conditional state logic on events | intentionally imperative | branch in the handler body (`onclick="…"` is ordinary JS) |
| Runtime restructuring (reparent, transient groups, add/remove nodes) | intentionally imperative | standard DOM (`createElement`/`append`); `attachChild()` to reparent preserving world transform |
| Bespoke per-frame procedural logic | intentionally imperative | `scene.onFrame` (scene-time driven) — never a bare rAF loop |
| Custom responses to dragging beyond the `draggable` attribute | intentionally imperative | the `draggable` attribute + drag event handlers |

<a id="reflex-table"></a>

## Reflex table

| Wrong by instinct | What actually happens | Write instead |
|---|---|---|
| Put `<sk-animation>` next to the thing that moves | keyframes for a property the host does not own do NOTHING | put the animation inside the element that OWNS the property: constraint props in the constraint, material colors in the material, stroke props in the stroke-paint, transforms on the node |
| easing on a keyframe eases INTO it | per-keyframe easing governs the segment STARTING at that keyframe | set `easing` on the segment's starting keyframe |
| bare numbers everywhere (`h="90"`, `duration="2000"`) | unitless angle/time attributes are invalid and IGNORED (the property keeps its default) | `.sk` attributes take CSS units (`h="90deg"`, `duration="2s"`); IDL properties take canonical numbers (`node.h` is radians) |
| `fill="forwards"` to keep an end state | a persistent forwards fill pins the property against all later writes | for a one-shot, commit the end state: `onfinish="this.commitStyles()"` (lowercase handler form); replay/cancel are separate topics — doc them |
| drive scene mutation with a `requestAnimationFrame` loop | a standalone rAF loop fights the engine and breaks deterministic capture | declarative `<sk-animation>`/constraints first; `animate()` for imperative WAAPI; `scene.onFrame` only for custom scene-time per-frame work |
| inline a material inside the mesh (`mesh.material = …`) | materials inlined into geometry are rejected; a paint without `material` is marked inert | declare materials with an `id` under `<sk-scene>`; bind from a paint child: `<sk-surface-paint material="#id">` |
| add a same-hue dark→light tonemap "for shading" | `base-color-darken-factor` already shades base→dark; stacking a same-hue tonemap double-darkens toward black | reserve tonemaps for genuinely different shadow/light hues; rely on the built-in darken factor otherwise |
| `offset` in milliseconds | `offset` is the normalized 0–1 fraction; `offset="500"` is invalid | `offset="0.5"` for halfway |
| a keyframe anywhere under the node | an `<sk-keyframe>` outside a direct `<sk-animation>` parent never runs | keyframes are DIRECT children of `<sk-animation>` |
| `:host` or bare `sk-scene` selectors for scene config | `:host` is ignored; bare `sk-scene` is document-global and leaks config across scenes | give the scene an id and scope by it: `sk-scene#my-id { --name: value }` |
| `scene.add(mesh)` and an injected `scene` variable | neither exists — the DOM IS the scene graph and scripts run as ordinary inline scripts | `document.createElement('sk-…')` + `append`; select with `document.querySelector` |

<a id="examples"></a>

## Verified examples

Fetch an example document before authoring an unfamiliar pattern; every example is a complete scene verified by the test suite.

- [guide:for-agents-example-primitives-materials](for-agents-example-primitives-materials.md) — primitives, by-id materials, surface/edge paints, ground, light
- [guide:for-agents-example-keyframes-easing](for-agents-example-keyframes-easing.md) — multi-keyframe animation, per-keyframe easing, infinite iterations
- [guide:for-agents-example-camera-fit](for-agents-example-camera-fit.md) — auto-fit orbit control, azimuth/elevation pose, focal length
- [guide:for-agents-example-constraints](for-agents-example-constraints.md) — orbit constraint, animated constraint property, target-at
- [guide:for-agents-example-cloners](for-agents-example-cloners.md) — cloner, grid points-source, clone paints, whole-cloner animation
- [guide:for-agents-example-fields](for-agents-example-fields.md) — by-id field resource, field-weighted point constraint, field animation
- [guide:for-agents-example-annotations](for-agents-example-annotations.md) — live annotation, world-space anchor, spline callout connector
- [guide:for-agents-example-scripting](for-agents-example-scripting.md) — dynamic node creation, IDL properties vs attributes, event handler

---

Related records: [`guide:for-agents-example-annotations`](guides/for-agents-example-annotations.md), [`guide:for-agents-example-camera-fit`](guides/for-agents-example-camera-fit.md), [`guide:for-agents-example-cloners`](guides/for-agents-example-cloners.md), [`guide:for-agents-example-constraints`](guides/for-agents-example-constraints.md), [`guide:for-agents-example-fields`](guides/for-agents-example-fields.md), [`guide:for-agents-example-keyframes-easing`](guides/for-agents-example-keyframes-easing.md), [`guide:for-agents-example-primitives-materials`](guides/for-agents-example-primitives-materials.md), [`guide:for-agents-example-scripting`](guides/for-agents-example-scripting.md)
