You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/src/arts/scene/README.md

5.3 KiB

scene ($scene)

Ambient-scene runtime — mounts self-contained animated scenes (WebGL shaders / canvas 2D) on a host element and owns, once, the citizenship every ad-hoc background used to reinvent or skip: frame loop, off-view pause, resize + DPR cap, mandatory reduced-motion policy, pointer smoothing, WebGL context loss/restore, a concurrent-scene budget and full teardown.

An Engine* art (public methods over private state, no $state), the exact sibling of arts/motion: the DOM arrives injected through the structural SceneDom port (requestFrame / cancelFrame / observeResize / observeIntersection / listen / prefersReducedMotion) — ActiveDom satisfies it; this art imports no other art.

import { createEngineScene } from '$scene';

const scene = createEngineScene({ dom: uix.dom, logger: uix.logger });
const handle = scene.mount(node, meshEffect, { speed: 0.4 });
handle.setParams({ speed: 1 });
handle.dispose();

The effect contract

An effect is a RESOURCE (data + draw code, typed params) — the pattern of sema's sounds.ts. Two shapes, one base:

Field Rule
reduce Mandatory ('static-frame' renders one frame at t=0 · 'hide' mounts nothing). The engine throws SceneConfigError without it.
defaults Authoring defaults; mount(_, _, params) shallow-merges over a copy.
driver: 'webgl' | 'webgl2' fragmentShader() + setup(gl, program) + update(gl, state, api). Standard uniforms provided: uResolution (vec3 w,h,aspect — device px), uTime, uMouse (normalized, Y up). GLSL1 fragments: no # directives, premultiplied output, vUv varying provided. GLSL3 fragments ship their own #version 300 es.
driver: 'canvas2d' setup(ctx, api) (deferred until the host is sized) + optional resize + draw(ctx, state, api). The ctx is pre-scaled to CSS px; the driver clears each frame.
custom pipeline (webgl) Opt-in for effects that are real GEOMETRY (displaced meshes, particle clouds, multi-pass post-processing) rather than a fullscreen fragment. Add vertexShader() (the driver compiles it instead of its fullscreen triangle) + draw(gl, state, api) (issues the draw call after update), and optionally glContext: { depth?, dprCap? } (a depth buffer for 3-D; a tighter DPR cap for chunky-pixel looks — the engine takes min(engine cap, effect cap)). The effect owns its buffers, created lazily in update keyed by params so a context restore rebuilds them (setup has no params). Context, loop, resize, standard uniforms, blending and teardown stay with the driver.
onPointerMove / onPointerDown / onPointerUp / onPointerLeave Opt-in — presence attaches HOST listeners through the port (the canvas never takes pointer events). On leave the engine also recenters the smoothed pointer in both spaces.

Per-frame the effect reads SceneApi: time (seconds, accumulated while running — off-view pauses never jump), dt (capped 0.1 s), live params, size (buffer px + dpr + CSS px) and the smoothed pointer.

Why an extension, not a new driver. The custom pipeline was introduced by the beam effect (3-D specular ribbons) as a justified extension of the SceneEffect contract rather than a third driver — the driver keeps owning all the citizenship; the effect only takes over geometry + the draw call. Six Tier B effects reuse it (particles, dither, grid, eter, pixel-blast, hyperspeed). The contract lives in types.ts; the design rationale in docs/process/PLAN-scene-ambient-pack.md.

Authoring a new effect

An effect is a shared resource; adding one to the Ambient pack is two files (the contract half lives here, the registration half in the pack):

  1. Write the effect at src/arts/scene/effects/{name}.ts against the SceneEffect contract above — a reduce policy, defaults, a driver, and colors as concrete hex (token resolution is the CONSUMER's job, per pack P-4 — this art imports no uix).
  2. Register it at src/packs/ambient/effects/{name}.ts (import = opt-in, tree-shaken): call registerAmbientEffect(...) and declare module to merge the param type into AmbientEffects. Then it is <Ambient effect="{name}">.

The pack tier + the P contract those pieces obey are in docs/architecture/packs.md.

Consumers + provenance

  • The Ambient pack (src/packs/ambient) mounts effects decoratively — tier + P contract in docs/architecture/packs.md.
  • The agentic phase will mount the same effects semantically through the canonical Aura component (delegate + sustain); at that point this engine is promoted to a uix.scene service (defineEngineScene factory) — deliberately NOT before (D4, docs/process/PLAN-scene-ambient-pack.md).
  • The seed collection lives untouched in web/routes/demos/animations as comparative reference; this runtime consolidates its 8 verbatim WebGLBackground copies + the aurora WebGL2 pattern, and fixes what none of the 45 handled (context loss, per-frame layout reads, mandatory reduce).

Tests

npx vitest run src/arts/scene

Lifecycle, reduce policies, unsupported-context degradation, budget warning and dispose idempotence run against a fake SceneDom (no browser needed).

Powered by TurnKey Linux.