# wity-scene A headless XML scene-graph library for spatial-temporal composition. Deterministic: f(scene, t) → ComputedFrame. Zero runtime dependencies. Runs in browser and Node.js. Current version: scene-core v1.0.0 · scene-headless v1.0.0 · scene-player v1.0.0 · scene-to-video v1.0.0 · scene-compose v1.0.0 · scene-to-pdf v1.0.0 · scene-to-pptx v1.0.0 · scene v1.0.0 Docs: https://www.wity.ai/stack/scene-graph/ ## Architecture ``` @wity/scene-core — parse · evaluate · serialize · validate. Zero DOM. ↓ @wity/scene-headless — authoring state: SceneStore, SelectionManager, HistoryManager, SnapState, TimelineState, Clipboard, commands. Zero DOM, zero framework. ↓ @wity/scene-player — HeadlessPlayer: drives evaluate(scene,t) at playback rate via RAF. Presentation-agnostic — wire any renderer to the 'frame' event. ↓ @wity/scene — convenience re-export of scene-core Server-side render pipeline (Node.js / Lambda only): @wity/scene-to-video — graphics compiler: ws-rect/ws-text/ws-image → node-canvas frame sequence → silent MP4 via FFmpeg. ws-video and ws-audio are NOT rendered here. ↓ (graphicsMp4Url) @wity/scene-compose — full compositor: ws-video clips + ws-audio tracks + graphicsMp4Url → FFmpeg filter_complex → final composited MP4 → S3 upload. @wity/scene-to-pdf — document renderer: evaluate(scene, ts) → node-canvas → pdf-lib → PDF. Supports standard (96 DPI) and print (300 DPI) variants. Multi-page via timestamps[]. @wity/scene-to-pptx — presentation renderer: evaluate(scene, ts) → node-canvas → pptxgenjs → .pptx. Each timestamp = one slide. Importable in PowerPoint, Keynote, Google Slides. Render services (Lambda-only, no npm package — all routed through witySceneRender): witySceneRender — render gateway: single public entry point. Routes to downstream Lambdas based on outputFormat. Callers never invoke downstream Lambdas directly. witySceneToVideo — internal: wraps @wity/scene-to-video witySceneCompose — internal: wraps @wity/scene-compose witySceneToPdf — internal: wraps @wity/scene-to-pdf witySceneToPptx — internal: wraps @wity/scene-to-pptx Analysis services (standalone Lambdas — NOT routed through witySceneRender): wityAudioProfile — audio loudness analyzer: parse scene → download ws-video/ws-audio media → FFmpeg decode PCM → windowed RMS measurement → per-element + power-mixed loudness profiles in dBFS. Used by AI tools for highlight extraction, loudness normalization, and sonic landscape understanding. ``` Core model: a scene document is a pure function of time. Call `evaluate(scene, t)` at any frame — no mutation, no playback state. Public entry point: witySceneRender (gateway Lambda). Callers send { sceneXml, outputFormat?, options? } to witySceneRender — it routes internally. Callers never invoke downstream Lambdas directly. Supported outputFormat values: "mp4" (default), "pdf", "pptx". mp4 routing: graphics-only scene → witySceneToVideo only. Mixed scene → witySceneToVideo then witySceneCompose. Media-only scene → witySceneCompose only (graphicsMp4Url: null). pdf routing: → witySceneToPdf (single call, options: ts, timestamps, variant, dpi). pptx routing: → witySceneToPptx (single call, options: ts, timestamps). Analysis services are separate — not routed through witySceneRender. Call wityAudioProfile directly with { sceneXml, options? }. Extensible: adding a new outputFormat means adding one Lambda + one entry in witySceneRender's PIPELINE_REGISTRY. No changes to existing Lambdas. ## Document structure ``` WityScene ├── WsCharacter[] (ws-cast — semantic metadata entities, non-rendered) └── WsLayer[] (z-ordered planes) └── WsElement[] (ws-text | ws-rect | ws-image | ws-video | ws-audio) ├── WsKeyframe[] (ws-keyframe — optional position/opacity keyframes inside ws-text, ws-rect, ws-image) └── WsCue[] (ws-cue — optional timed speech/subtitle cues inside ws-video or ws-audio, non-rendered) ``` ## Quick start — evaluate only ```js import { parse, evaluate } from '@wity/scene-core'; const scene = parse(` Opening Night `); const frame = evaluate(scene, 1.0); // ComputedFrame at t=1s for (const el of frame.elements) { if (el.visible) console.log(el.tag, el.x, el.y, el.content); } ``` ## Quick start — HeadlessPlayer ```js import { HeadlessPlayer } from '@wity/scene-player'; const player = new HeadlessPlayer(); player.loadXml(xml); player.on('frame', ({ frame }) => renderFrame(frame)); player.play(); // Mutations take effect on the next frame — no reload: player.updateElement(id, { src: videoUrl }); player.updateLayer(layerId, { begin: 2.0, dur: 4.0 }); // Persist after mutations: const xml = player.getXml(); ``` --- ## Documentation pages ### Guides - [Overview](/guide/overview) — core model, document structure, rendering targets - [Schema v1.0](/guide/schema) — full XML attribute reference for all element types - [Rendering](/guide/rendering) — HTML/CSS and Canvas 2D renderer examples, ComputedFrame shape - [Deployment](/guide/deployment) — server-side render pipeline, Lambda configs, routing ### API Reference - [@wity/scene-core](/packages/scene-core) — parse, evaluate, serialize, validate, resolveUnit, all types - [@wity/scene-player](/packages/scene-player) — HeadlessPlayer: loadXml, play/pause/seek, mutation API, 'frame' events - [@wity/scene-to-video](/packages/scene-to-video) — compile(sceneXml, fontManifest, options): graphics-only MP4 compiler - [@wity/scene-compose](/packages/scene-compose) — compose(sceneXml, graphicsMp4Url, options): full compositing pass - [@wity/scene-to-pdf](/packages/scene-to-pdf) — render(sceneXml, fontManifest, options): scene snapshot → PDF - [@wity/scene-to-pptx](/packages/scene-to-pptx) — render(sceneXml, fontManifest, options): scene snapshots → PPTX - [@wity/scene](/packages/scene) — re-export package ### Services - [witySceneRender](/services/gateway) — render gateway: single public entry point, routes to mp4/pdf/pptx pipelines - [wityAudioProfile](/services/audio-profile) — windowed RMS loudness analysis: per-element + power-mixed dBFS profiles