/** * Immutable semantic recipes for Orby. * * A sprite is intentionally data, not a baked animation. Renderers combine its * glyph silhouette, field stack, switching operator, transition contract, and * interaction mappings. This keeps the catalogue portable across Canvas 2D, * WebGL, SVG exporters, sprite-sheet bakers, and reduced-motion renderers. */ const deepFreeze = (value, seen = new WeakSet()) => { if ( value === null || (typeof value !== "object" && typeof value !== "function") || seen.has(value) ) { return value; } seen.add(value); for (const child of Object.values(value)) deepFreeze(child, seen); return Object.freeze(value); }; export const FIELD_IDS = Object.freeze([ "fbm", "ridged", "domain-warp", "curl", "flow", "worley", "voronoi", "plasma", "interference", "vortex", "metaballs", "caustics", "strata", "radar", "constellation", "liquid", "electric", "ripple", "kaleidoscope", ]); const FIELD_ID_SET = new Set(FIELD_IDS); const PALETTE_SOURCE = { neutral: { name: "neutral", background: "#07090c", shadow: "#1b222b", ink: "#eef3f8", accent: "#93cfff", }, info: { name: "info", background: "#071019", shadow: "#15344a", ink: "#edf8ff", accent: "#4fc3ff", }, success: { name: "success", background: "#07110e", shadow: "#16392d", ink: "#f0fff9", accent: "#54d99b", }, warning: { name: "warning", background: "#151006", shadow: "#493514", ink: "#fff9e9", accent: "#f5b94c", }, danger: { name: "danger", background: "#15090b", shadow: "#4c1c25", ink: "#fff2f4", accent: "#ff647c", }, celebration: { name: "celebration", background: "#100a19", shadow: "#352052", ink: "#fff7ff", accent: "#cf86ff", }, }; export const PALETTES = deepFreeze(PALETTE_SOURCE); function layer(field, weight, scale, blend = "add", options = {}) { if (!FIELD_ID_SET.has(field)) { throw new TypeError(`Unknown field "${field}" in built-in sprite recipe.`); } return { field, weight, scale, blend, ...(Number.isFinite(options.segments) ? { segments: Math.round(options.segments) } : {}), phase: options.phase ?? 0, speed: options.speed ?? 1, contrast: options.contrast ?? 1, warp: options.warp ?? 0, }; } const defaultPointer = { enabled: true, minSize: 20, effect: "steer-light", strength: 0.16, reducedMotion: false, }; const defaultPress = { enabled: true, effect: "spring-compress", strength: 0.07, durationMs: 180, }; function defineSprite({ id, category, meaning, glyph, field, fieldMix, composition = {}, palette = "neutral", threshold = 0.5, density = 0.72, speed = 1, pixelShape = "rounded-square", pixelGap = 0.12, pixelScale = 0.94, silhouetteFloor = 0.38, pixelSwitch = {}, timeline = {}, transition = {}, interactions = {}, reducedMotion = {}, labels, terminal = false, urgency = "normal", }) { if (!FIELD_ID_SET.has(field)) { throw new TypeError(`Unknown primary field "${field}" for "${id}".`); } if (!labels?.default) { throw new TypeError(`Sprite "${id}" must define labels.default.`); } const mix = fieldMix?.length ? fieldMix : [layer(field, 1, 1, "replace")]; const totalWeight = mix.reduce((sum, item) => sum + item.weight, 0); const compositionConfig = typeof composition === "string" ? { mode: composition } : composition && typeof composition === "object" ? composition : {}; const recipe = { schemaVersion: 1, id, semantic: { category, meaning, terminal, urgency, announce: labels.live ?? (urgency === "high" ? "assertive" : "polite"), decorativeCanvas: true, monochromeSafe: true, minimumReadableSize: 16, }, glyph, field, composition: { mode: "glyph", ...compositionConfig, }, fieldMix: mix.map((item) => ({ ...item, weight: totalWeight > 0 ? item.weight / totalWeight : 0, })), palette: PALETTES[palette] ?? PALETTES.neutral, threshold, density, speed, pixel: { shape: pixelShape, gap: pixelGap, scale: pixelScale, quantize: "1bit", silhouetteFloor, }, pixelSwitch: { mode: pixelSwitch.mode ?? "threshold-hysteresis", dither: pixelSwitch.dither ?? "bayer8", hysteresis: pixelSwitch.hysteresis ?? 0.055, temporalRate: pixelSwitch.temporalRate ?? speed, neighborhood: pixelSwitch.neighborhood ?? 0, direction: pixelSwitch.direction ?? "field", monotonic: pixelSwitch.monotonic ?? false, maxContrastFlashesPerSecond: pixelSwitch.maxContrastFlashesPerSecond ?? 3, }, timeline: { mode: timeline.mode ?? "loop", durationMs: timeline.durationMs ?? Math.round(1000 / Math.max(speed, 0.01)), macroCycleMs: timeline.macroCycleMs ?? null, holdMs: timeline.holdMs ?? 0, replay: timeline.replay ?? "continuous", next: timeline.next ?? null, }, transition: { enter: transition.enter ?? "field-morph", exit: transition.exit ?? "field-morph", durationMs: transition.durationMs ?? 360, exitDurationMs: transition.exitDurationMs ?? transition.durationMs ?? 360, preservePhase: transition.preservePhase ?? true, preserveBuffer: transition.preserveBuffer ?? false, interruptible: transition.interruptible ?? true, }, interactions: { pointer: { ...defaultPointer, ...(interactions.pointer ?? {}), }, press: { ...defaultPress, ...(interactions.press ?? {}), }, signals: interactions.signals ?? [], }, reducedMotion: { mode: reducedMotion.mode ?? "representative-phase", phase: reducedMotion.phase ?? 0.25, speed: 0, transition: reducedMotion.transition ?? "crossfade", transitionMs: reducedMotion.transitionMs ?? 120, pointer: false, spatialMotion: false, representation: reducedMotion.representation ?? `static ${labels.default.toLowerCase()} glyph`, dataUpdates: reducedMotion.dataUpdates ?? "discrete", }, labels: { default: labels.default, aria: labels.aria ?? labels.default, live: labels.live ?? (urgency === "high" ? "assertive" : "polite"), value: labels.value ?? null, variants: labels.variants ?? null, }, }; return deepFreeze(recipe); } const RECIPES = [ defineSprite({ id: "ai.idle", category: "presence", meaning: "ready and available", glyph: "idle", field: "fbm", composition: { gestaltBoundary: true, gestaltOpenness: 0.58, }, fieldMix: [ layer("fbm", 0.72, 1.15, "replace", { speed: 0.2, contrast: 0.9 }), layer("caustics", 0.28, 1.7, "soft-light", { speed: -0.09, phase: 0.19, }), ], palette: "neutral", threshold: 0.51, density: 0.68, speed: 0.14, pixelShape: "rounded-square", pixelSwitch: { mode: "ordered-dither", dither: "bayer8", hysteresis: 0.045, temporalRate: 0.1, direction: "field", }, timeline: { durationMs: 7200, macroCycleMs: 14400, replay: "continuous", }, transition: { enter: "field-morph", exit: "field-morph", durationMs: 520, preservePhase: true, }, interactions: { pointer: { effect: "steer-highlight", strength: 0.14 }, press: { effect: "spring-compress", strength: 0.055 }, }, reducedMotion: { phase: 0.18, representation: "static side-lit lunar sphere with a persistent rim", }, labels: { default: "Ready", live: "polite" }, }), defineSprite({ id: "ai.ambient-idle", category: "ambient", meaning: "calm field-only presence", glyph: "idle", field: "fbm", composition: { mode: "field-orb", gestaltBoundary: true, gestaltOpenness: 0.46, lighting: "crescent", radius: 0.88, fieldContrast: 1.26, fieldDetail: 0.86, lightOrbit: 0.1, breathe: 0.045, rim: 0.055, }, fieldMix: [ layer("fbm", 0.62, 1.14, "replace", { speed: 0.18, contrast: 1.24, }), layer("caustics", 0.38, 2.05, "soft-light", { speed: -0.11, contrast: 1.08, phase: 0.31, }), ], palette: "neutral", threshold: 0.49, density: 0.76, speed: 0.22, pixelShape: "rounded-square", pixelScale: 0.9, pixelSwitch: { mode: "ordered-dither", dither: "bayer8", hysteresis: 0.045, temporalRate: 0.18, direction: "surface-drift", }, timeline: { durationMs: 7200, macroCycleMs: 14400, replay: "continuous", }, transition: { enter: "field-morph", exit: "field-morph", durationMs: 520, preservePhase: true, }, interactions: { pointer: { effect: "steer-light", strength: 0.18 }, press: { effect: "surface-ripple", strength: 0.09 }, }, reducedMotion: { phase: 0.18, representation: "static side-lit dithered field orb", }, labels: { default: "Ambient ready", live: "polite" }, }), defineSprite({ id: "ai.ambient-thinking", category: "ambient", meaning: "reasoning expressed only through an animated field", glyph: "thinking", field: "domain-warp", composition: { mode: "field-orb", gestaltBoundary: true, gestaltOpenness: 0.62, lighting: "sphere", radius: 0.84, fieldContrast: 1.48, fieldDetail: 0.96, lightOrbit: 0.2, breathe: 0.09, rim: 0.045, }, fieldMix: [ layer("domain-warp", 0.46, 1.08, "replace", { speed: 0.52, contrast: 1.34, warp: 0.7, }), layer("curl", 0.32, 1.52, "add", { speed: -0.38, contrast: 1.22, }), layer("ridged", 0.22, 2.18, "soft-light", { speed: 0.24, contrast: 1.36, }), ], palette: "info", threshold: 0.5, density: 0.8, speed: 0.72, pixelShape: "rounded-square", pixelScale: 0.89, pixelSwitch: { mode: "temporal-blue-noise", dither: "blue-noise64", hysteresis: 0.048, temporalRate: 0.68, direction: "counter-flow", }, timeline: { durationMs: 4600, macroCycleMs: 9200, replay: "continuous", }, transition: { enter: "radial-cascade", exit: "field-morph", durationMs: 460, preservePhase: true, }, interactions: { pointer: { effect: "bend-flow-and-light", strength: 0.16 }, press: { effect: "damped-vortex", strength: 0.18, durationMs: 460 }, signals: [ { name: "reasoning.depth", mapsTo: "shellEnergy", range: [0, 1] }, ], }, reducedMotion: { phase: 0.34, representation: "static front-lit dithered reasoning field", }, labels: { default: "Ambient thinking", live: "polite" }, }), defineSprite({ id: "ai.ambient-thinking-symmetric", category: "ambient", meaning: "reasoning expressed through a clean, symmetrical animated field", glyph: "thinking", field: "kaleidoscope", composition: { mode: "field-orb", lighting: "flat", radius: 0.87, fieldContrast: 1.32, fieldDetail: 0.84, lightOrbit: 0, breathe: 0.045, rim: 0.04, }, fieldMix: [ layer("kaleidoscope", 0.74, 0.82, "replace", { segments: 8, speed: 0.2, contrast: 1.25, }), layer("kaleidoscope", 0.26, 1.42, "soft-light", { segments: 8, speed: -0.12, contrast: 1.08, }), ], palette: "info", threshold: 0.5, density: 0.78, speed: 0.5, pixelShape: "square", pixelGap: 0.14, pixelScale: 0.9, pixelSwitch: { mode: "threshold-hysteresis", dither: "bayer8", hysteresis: 0.058, temporalRate: 0.34, direction: "radial", }, timeline: { durationMs: 6400, macroCycleMs: 12800, replay: "continuous", }, transition: { enter: "radial-cascade", exit: "field-morph", durationMs: 500, preservePhase: true, }, interactions: { pointer: { enabled: false }, press: { enabled: false }, signals: [ { name: "reasoning.depth", mapsTo: "shellEnergy", range: [0, 1] }, ], }, reducedMotion: { phase: 0.25, representation: "static symmetrical reasoning field", }, labels: { default: "Ambient thinking — symmetric", live: "polite" }, }), defineSprite({ id: "ai.ambient-speaking", category: "ambient", meaning: "voice output expressed only through an animated field", glyph: "speaking", field: "interference", composition: { mode: "field-orb", gestaltBoundary: true, gestaltOpenness: 0.5, lighting: "gibbous", radius: 0.87, fieldContrast: 1.38, fieldDetail: 0.92, lightOrbit: 0.13, breathe: 0.065, voice: 0.2, rim: 0.05, }, fieldMix: [ layer("interference", 0.46, 1.26, "replace", { speed: 1.05, contrast: 1.28, }), layer("plasma", 0.34, 1.7, "screen", { speed: -0.72, contrast: 1.18, }), layer("ripple", 0.2, 1.12, "add", { speed: 0.82, contrast: 1.24, }), ], palette: "info", threshold: 0.48, density: 0.82, speed: 1.02, pixelShape: "circle", pixelScale: 0.9, pixelSwitch: { mode: "ordered-dither", dither: "bayer8", hysteresis: 0.042, temporalRate: 0.96, direction: "audio-surface", }, timeline: { mode: "signal-reactive", durationMs: 1900, macroCycleMs: 5700, }, transition: { enter: "radial-cascade", exit: "field-morph", durationMs: 300, preservePhase: true, }, interactions: { pointer: { effect: "steer-light", strength: 0.13 }, press: { enabled: false }, signals: [ { name: "audio.level", mapsTo: "envelope", range: [0, 1], decayMs: 220, }, { name: "audio.syllable", mapsTo: "bandEnergy", range: [0, 1] }, ], }, reducedMotion: { mode: "data-step", phase: 0.38, representation: "static gibbous field with discrete audio levels", dataUpdates: "three-level-step", }, labels: { default: "Ambient speaking", live: "polite" }, }), defineSprite({ id: "ai.listening", category: "activity", meaning: "capturing voice or input", glyph: "listening", field: "interference", fieldMix: [ layer("interference", 0.46, 1.4, "replace", { speed: 0.68, contrast: 1.18, }), layer("radar", 0.32, 1.05, "add", { speed: -0.46 }), layer("liquid", 0.22, 1.8, "soft-light", { speed: 0.3 }), ], palette: "info", threshold: 0.48, density: 0.77, speed: 0.78, pixelShape: "circle", pixelSwitch: { mode: "radial-cascade", dither: "bayer8", hysteresis: 0.05, direction: "inward", }, timeline: { durationMs: 2400, macroCycleMs: 4800 }, transition: { enter: "sdf-wavefront", exit: "radial-cascade", durationMs: 340, preservePhase: true, }, interactions: { pointer: { effect: "ripple-bias", strength: 0.12 }, press: { effect: "emit-inbound-ripple", strength: 0.18 }, signals: [ { name: "audio.level", mapsTo: "energy", range: [0, 1], decayMs: 90 }, { name: "input.pulse", mapsTo: "ripple", range: [0, 1] }, ], }, reducedMotion: { phase: 0.36, representation: "static listening ring with discrete level steps", dataUpdates: "three-level-step", }, labels: { default: "Listening", live: "polite" }, }), defineSprite({ id: "ai.thinking", category: "activity", meaning: "reasoning", glyph: "thinking", field: "flow", fieldMix: [ layer("flow", 0.47, 1.35, "replace", { speed: 0.82, contrast: 1.08, }), layer("vortex", 0.35, 1.05, "add", { speed: -0.62, warp: 0.22 }), layer("fbm", 0.18, 2.1, "soft-light", { speed: 0.25 }), ], palette: "info", threshold: 0.5, density: 0.74, speed: 0.92, pixelShape: "rounded-square", pixelSwitch: { mode: "temporal-blue-noise", dither: "blue-noise64", hysteresis: 0.062, temporalRate: 0.82, }, timeline: { durationMs: 3100, macroCycleMs: 6200 }, transition: { enter: "field-morph", exit: "field-morph", durationMs: 430, preservePhase: true, }, interactions: { pointer: { effect: "bend-flow", strength: 0.13 }, press: { effect: "damped-vortex", strength: 0.2, durationMs: 460 }, }, reducedMotion: { phase: 0.3, representation: "static faceted spiral", }, labels: { default: "Thinking", live: "polite" }, }), defineSprite({ id: "ai.thinking-deep", category: "activity", meaning: "deliberate extended reasoning", glyph: "thinking-deep", field: "domain-warp", fieldMix: [ layer("domain-warp", 0.46, 0.95, "replace", { speed: 0.34, warp: 0.48, }), layer("ridged", 0.31, 1.8, "multiply", { speed: 0.18, contrast: 1.24, }), layer("vortex", 0.23, 0.72, "add", { speed: -0.27 }), ], palette: "info", threshold: 0.52, density: 0.79, speed: 0.38, pixelShape: "diamond", pixelGap: 0.1, pixelSwitch: { mode: "neighbor-propagation", dither: "bayer8", hysteresis: 0.07, temporalRate: 0.36, neighborhood: 8, direction: "shell-to-shell", }, timeline: { durationMs: 9600, macroCycleMs: 19200, replay: "continuous", }, transition: { enter: "field-morph", exit: "field-morph", durationMs: 680, preservePhase: true, }, interactions: { pointer: { effect: "separate-shells", strength: 0.09 }, press: { effect: "pinch-core", strength: 0.16, durationMs: 520 }, signals: [ { name: "reasoning.depth", mapsTo: "shellEnergy", range: [0, 1], }, ], }, reducedMotion: { phase: 0.42, representation: "static nested rings with a brighter inner core", }, labels: { default: "Reasoning deeply", live: "polite" }, }), defineSprite({ id: "ai.still-working", category: "activity", meaning: "work is taking longer than expected", glyph: "still-working", field: "strata", fieldMix: [ layer("strata", 0.44, 1.2, "replace", { speed: 0.21, contrast: 1.08, }), layer("radar", 0.34, 0.82, "add", { speed: 0.17 }), layer("flow", 0.22, 1.55, "soft-light", { speed: 0.18 }), ], palette: "neutral", threshold: 0.5, density: 0.71, speed: 0.24, pixelShape: "rounded-square", pixelSwitch: { mode: "radial-cascade", dither: "bayer8", hysteresis: 0.055, temporalRate: 0.62, direction: "outward-beacon", }, timeline: { durationMs: 3200, macroCycleMs: 3200, replay: "continuous", }, transition: { enter: "field-morph", exit: "field-morph", durationMs: 560, preservePhase: false, }, interactions: { pointer: { effect: "expand-beacon", strength: 0.1 }, press: { effect: "host-action", strength: 0.05 }, signals: [ { name: "latency.elapsed", mapsTo: "beaconEnergy", range: [0, 1] }, ], }, reducedMotion: { phase: 0.46, representation: "upright hourglass with settled sand and flip guides", }, labels: { default: "Still working", live: "polite" }, }), defineSprite({ id: "ai.loading", category: "activity", meaning: "indeterminate startup or wait", glyph: "loading", field: "plasma", fieldMix: [ layer("plasma", 0.58, 1.15, "replace", { speed: 1.15, contrast: 1.16, }), layer("flow", 0.42, 1.65, "add", { speed: 0.88 }), ], palette: "info", threshold: 0.52, density: 0.7, speed: 1.38, pixelShape: "square", pixelSwitch: { mode: "ordered-dither", dither: "bayer8", hysteresis: 0.052, temporalRate: 1.3, direction: "radial-tail", }, timeline: { durationMs: 1440, macroCycleMs: 5760 }, transition: { enter: "radial-cascade", exit: "field-morph", durationMs: 280, preservePhase: true, }, interactions: { pointer: { effect: "steer-highlight", strength: 0.08 }, press: { enabled: false }, }, reducedMotion: { phase: 0.63, representation: "static segmented loading ring", }, labels: { default: "Loading", live: "polite" }, }), defineSprite({ id: "ai.progress", category: "activity", meaning: "determinate completion progress", glyph: "progress", field: "strata", fieldMix: [ layer("strata", 0.54, 1.25, "replace", { speed: 0.25, contrast: 1.1, }), layer("constellation", 0.46, 1.9, "add", { speed: 0.1, phase: 0.37, }), ], palette: "info", threshold: 0.47, density: 0.84, speed: 0.32, pixelShape: "circle", pixelSwitch: { mode: "temporal-blue-noise", dither: "blue-noise64", hysteresis: 0.045, temporalRate: 0.28, direction: "contour-forward", monotonic: true, }, timeline: { mode: "data-driven", durationMs: 240, replay: "on-update" }, transition: { enter: "contour-trace", exit: "path-draw", durationMs: 320, preservePhase: true, }, interactions: { pointer: { effect: "reveal-value", strength: 0 }, press: { effect: "spring-compress", strength: 0.045 }, signals: [ { name: "progress", mapsTo: "progress", range: [0, 1], monotonic: true, }, ], }, reducedMotion: { mode: "data-step", phase: 0, representation: "static progress contour updated from data", dataUpdates: "discrete-monotonic", }, labels: { default: "In progress", value: "{percent}% complete", live: "polite", }, }), defineSprite({ id: "ai.generating", category: "activity", meaning: "producing content", glyph: "generating", field: "electric", fieldMix: [ layer("electric", 0.4, 1.75, "replace", { speed: 0.84, contrast: 1.23, }), layer("metaballs", 0.34, 1.1, "add", { speed: 0.54, warp: 0.18, }), layer("constellation", 0.26, 2.05, "screen", { speed: 0.38, }), ], palette: "info", threshold: 0.5, density: 0.78, speed: 0.88, pixelShape: "diamond", pixelSwitch: { mode: "curl-advect", dither: "blue-noise64", hysteresis: 0.058, temporalRate: 0.8, neighborhood: 4, direction: "core-to-target", }, timeline: { mode: "signal-reactive", durationMs: 3600, macroCycleMs: 7200, }, transition: { enter: "seeded-dissolve", exit: "field-morph", durationMs: 460, preservePhase: true, }, interactions: { pointer: { effect: "attract-unlocked-pixels", strength: 0.11 }, press: { effect: "core-pulse", strength: 0.08 }, signals: [ { name: "token", mapsTo: "spark", range: [0, 1], bounded: true }, { name: "chunk", mapsTo: "spark", range: [0, 1], bounded: true }, { name: "frame", mapsTo: "spark", range: [0, 1], bounded: true }, { name: "stream.energy", mapsTo: "activity", range: [0, 1] }, ], }, reducedMotion: { mode: "data-step", phase: 0.72, representation: "target glyph filled in non-moving chunks", dataUpdates: "chunked-fill", }, labels: { default: "Generating", live: "polite" }, }), defineSprite({ id: "ai.searching", category: "activity", meaning: "searching or retrieving information", glyph: "searching", field: "radar", fieldMix: [ layer("radar", 0.53, 0.95, "replace", { speed: 0.72, contrast: 1.18, }), layer("worley", 0.29, 1.62, "add", { speed: 0.2 }), layer("caustics", 0.18, 2.1, "screen", { speed: 0.16 }), ], palette: "info", threshold: 0.49, density: 0.73, speed: 0.74, pixelShape: "circle", pixelSwitch: { mode: "sdf-wavefront", dither: "bayer8", hysteresis: 0.057, temporalRate: 0.72, direction: "radar-sweep", }, timeline: { durationMs: 3300, macroCycleMs: 6600 }, transition: { enter: "radial-cascade", exit: "field-morph", durationMs: 380, preservePhase: true, }, interactions: { pointer: { effect: "bias-decorative-beam", strength: 0.1 }, press: { enabled: false }, signals: [ { name: "search.hit", mapsTo: "worleyBloom", range: [0, 1], decayMs: 620, }, { name: "search.results", mapsTo: "hitCount", range: [0, 8] }, ], }, reducedMotion: { phase: 0.2, representation: "static magnifier with discrete result dots", dataUpdates: "result-dot", }, labels: { default: "Searching", live: "polite" }, }), defineSprite({ id: "ai.tool-use", category: "activity", meaning: "executing a tool or action", glyph: "tool-use", field: "strata", fieldMix: [ layer("strata", 0.43, 1.3, "replace", { speed: 0.46, contrast: 1.12, }), layer("electric", 0.34, 1.8, "add", { speed: 0.68 }), layer("voronoi", 0.23, 1.05, "multiply", { speed: 0.22 }), ], palette: "neutral", threshold: 0.52, density: 0.8, speed: 0.68, pixelShape: "square", pixelSwitch: { mode: "neighbor-propagation", dither: "bayer8", hysteresis: 0.062, temporalRate: 0.62, neighborhood: 6, direction: "gear-step", }, timeline: { mode: "signal-reactive", durationMs: 2400, macroCycleMs: 7200, }, transition: { enter: "axis-flip", exit: "field-morph", durationMs: 420, preservePhase: true, }, interactions: { pointer: { effect: "tilt-light", strength: 0.13 }, press: { effect: "mechanism-compress", strength: 0.09 }, signals: [ { name: "tool.step", mapsTo: "toothPulse", range: [0, 1] }, { name: "tool.wait", mapsTo: "stepHold", range: [0, 1] }, { name: "tool.result", mapsTo: "coreState", range: [0, 1] }, ], }, reducedMotion: { phase: 0.125, representation: "static gear with data-driven step dots", dataUpdates: "step-dot", }, labels: { default: "Using a tool", live: "polite" }, }), defineSprite({ id: "ai.speaking", category: "activity", meaning: "producing voice output", glyph: "speaking", field: "interference", fieldMix: [ layer("interference", 0.44, 1.2, "replace", { speed: 1.05, contrast: 1.16, }), layer("plasma", 0.33, 1.62, "add", { speed: 0.74 }), layer("liquid", 0.23, 0.92, "soft-light", { speed: 0.55, warp: 0.2, }), ], palette: "info", threshold: 0.47, density: 0.81, speed: 1.08, pixelShape: "circle", pixelSwitch: { mode: "radial-cascade", dither: "bayer8", hysteresis: 0.05, temporalRate: 1.02, direction: "outward-audio", }, timeline: { mode: "signal-reactive", durationMs: 1900, macroCycleMs: 5700, }, transition: { enter: "sdf-wavefront", exit: "radial-cascade", durationMs: 260, preservePhase: true, }, interactions: { pointer: { effect: "steer-light", strength: 0.11 }, press: { enabled: false }, signals: [ { name: "audio.level", mapsTo: "envelope", range: [0, 1], decayMs: 240, }, { name: "audio.syllable", mapsTo: "bandEnergy", range: [0, 1] }, ], }, reducedMotion: { mode: "data-step", phase: 0.38, representation: "static speaker with three discrete level states", dataUpdates: "three-level-step", }, labels: { default: "Speaking", live: "polite" }, }), defineSprite({ id: "ai.awaiting-input", category: "activity", meaning: "user action is required", glyph: "awaiting-input", field: "fbm", fieldMix: [ layer("fbm", 0.72, 0.92, "replace", { speed: 0.18, contrast: 0.94, }), layer("caustics", 0.28, 1.55, "soft-light", { speed: 0.1, contrast: 0.9, }), ], palette: "info", threshold: 0.48, density: 0.8, speed: 0.22, pixelShape: "rounded-square", pixelSwitch: { mode: "threshold-hysteresis", dither: "bayer8", hysteresis: 0.052, temporalRate: 0.08, direction: "focus", }, timeline: { mode: "hold", durationMs: 1800, macroCycleMs: 10800, replay: "two-pulses-then-hold", }, transition: { enter: "radial-cascade", exit: "field-morph", durationMs: 420, preservePhase: false, }, interactions: { pointer: { effect: "turn-aperture", strength: 0.1 }, press: { effect: "acknowledge", strength: 0.06, durationMs: 160, emits: "activate", }, signals: [{ name: "activate", mapsTo: "acknowledgement", range: [0, 1] }], }, reducedMotion: { phase: 0.44, representation: "static question mark inside a firefly focus ring", }, labels: { default: "Your input is needed", live: "polite" }, }), defineSprite({ id: "status.success", category: "status", meaning: "completed successfully", glyph: "success", field: "caustics", fieldMix: [ layer("caustics", 0.57, 1.3, "replace", { speed: 0.72, contrast: 1.15, }), layer("constellation", 0.43, 1.75, "screen", { speed: 0.35 }), ], palette: "success", threshold: 0.46, density: 0.9, speed: 0.82, pixelShape: "rounded-square", pixelSwitch: { mode: "path-draw", dither: "blue-noise64", hysteresis: 0.042, temporalRate: 1, direction: "tail-to-head", monotonic: false, }, timeline: { mode: "one-shot", durationMs: 500, holdMs: 800, replay: "on-enter", }, transition: { enter: "path-draw", exit: "field-morph", durationMs: 500, exitDurationMs: 300, preservePhase: false, }, interactions: { pointer: { effect: "edge-sparkle", strength: 0.04 }, press: { enabled: false }, }, reducedMotion: { mode: "static", phase: 1, representation: "static checkmark", transitionMs: 120, }, labels: { default: "Completed", aria: "Completed successfully", live: "polite" }, terminal: true, }), defineSprite({ id: "status.warning", category: "status", meaning: "attention is needed for a nonfatal issue", glyph: "warning", field: "strata", fieldMix: [ layer("strata", 0.58, 1.18, "replace", { speed: 0.38, contrast: 1.22, }), layer("interference", 0.42, 1.55, "add", { speed: 0.33 }), ], palette: "warning", threshold: 0.49, density: 0.86, speed: 0.42, pixelShape: "diamond", pixelSwitch: { mode: "contour-trace", dither: "bayer8", hysteresis: 0.065, temporalRate: 0.38, direction: "bottom-to-contour", }, timeline: { mode: "one-shot-hold", durationMs: 1100, holdMs: 1600, replay: "two-pulses-then-hold", }, transition: { enter: "sdf-wavefront", exit: "field-morph", durationMs: 430, preservePhase: false, }, interactions: { pointer: { effect: "lean-highlight", strength: 0.08 }, press: { effect: "contained-ripple", strength: 0.08 }, }, reducedMotion: { mode: "static", phase: 1, representation: "static outlined triangle and exclamation", }, labels: { default: "Warning", live: "polite" }, urgency: "medium", }), defineSprite({ id: "status.error", category: "status", meaning: "operation failed", glyph: "error", field: "electric", fieldMix: [ layer("electric", 0.61, 1.4, "replace", { speed: 0.82, contrast: 1.3, }), layer("ridged", 0.39, 1.85, "multiply", { speed: 0.35, contrast: 1.22, }), ], palette: "danger", threshold: 0.51, density: 0.88, speed: 0.78, pixelShape: "square", pixelSwitch: { mode: "axis-flip", dither: "bayer8", hysteresis: 0.055, temporalRate: 0.9, direction: "diagonal-fracture", }, timeline: { mode: "one-shot-hold", durationMs: 410, holdMs: 1200, replay: "on-enter", }, transition: { enter: "axis-flip", exit: "seeded-dissolve", durationMs: 410, preservePhase: false, }, interactions: { pointer: { effect: "edge-repel", strength: 0.055 }, press: { enabled: false }, }, reducedMotion: { mode: "static", phase: 1, representation: "static X", transitionMs: 100, }, labels: { default: "Error", aria: "Operation failed", live: "assertive" }, terminal: true, urgency: "high", }), defineSprite({ id: "status.paused", category: "status", meaning: "work is suspended and can resume", glyph: "paused", field: "fbm", fieldMix: [ layer("fbm", 0.55, 1.1, "replace", { speed: 0 }), layer("strata", 0.45, 1.4, "multiply", { speed: 0 }), ], palette: "neutral", threshold: 0.5, density: 0.9, speed: 0, pixelShape: "rounded-square", pixelSwitch: { mode: "axis-flip", dither: "bayer8", hysteresis: 0.08, temporalRate: 0, direction: "columns", }, timeline: { mode: "hold", durationMs: 1, replay: "resume-reversible", }, transition: { enter: "axis-flip", exit: "axis-flip", durationMs: 280, preservePhase: true, preserveBuffer: true, }, interactions: { pointer: { effect: "steer-light-only", strength: 0.06 }, press: { effect: "bar-squeeze", strength: 0.045 }, signals: [{ name: "resume", mapsTo: "reverseTransition", range: [0, 1] }], }, reducedMotion: { mode: "static", phase: 1, representation: "static pause bars", }, labels: { default: "Paused", live: "polite" }, }), defineSprite({ id: "status.cancelled", category: "status", meaning: "operation was stopped", glyph: "cancelled", field: "vortex", fieldMix: [ layer("vortex", 0.57, 0.88, "replace", { speed: -0.68, contrast: 1.12, }), layer("metaballs", 0.43, 1.25, "add", { speed: -0.42 }), ], palette: "neutral", threshold: 0.52, density: 0.85, speed: 0.62, pixelShape: "square", pixelSwitch: { mode: "seeded-dissolve", dither: "blue-noise64", hysteresis: 0.065, temporalRate: 0.72, direction: "reverse-to-center", }, timeline: { mode: "one-shot-hold", durationMs: 360, holdMs: 900, replay: "on-enter", }, transition: { enter: "seeded-dissolve", exit: "seeded-dissolve", durationMs: 360, preservePhase: false, }, interactions: { pointer: { enabled: false }, press: { effect: "spring-compress", strength: 0.035 }, }, reducedMotion: { mode: "static", phase: 1, representation: "static stop square", }, labels: { default: "Cancelled", live: "polite" }, terminal: true, }), defineSprite({ id: "status.offline", category: "status", meaning: "disconnected or unavailable", glyph: "offline", field: "radar", fieldMix: [ layer("radar", 0.56, 0.72, "replace", { speed: 0.16, contrast: 1.16, }), layer("electric", 0.44, 1.55, "add", { speed: 0.11 }), ], palette: "neutral", threshold: 0.53, density: 0.72, speed: 0.18, pixelShape: "rounded-square", pixelSwitch: { mode: "path-draw", dither: "bayer8", hysteresis: 0.075, temporalRate: 0.16, direction: "bridge-and-retreat", }, timeline: { mode: "signal-reactive", durationMs: 4200, macroCycleMs: 8400, replay: "continuous", }, transition: { enter: "contour-trace", exit: "field-morph", durationMs: 480, preservePhase: true, }, interactions: { pointer: { effect: "weak-echo", strength: 0.045 }, press: { effect: "host-retry", strength: 0.04 }, signals: [ { name: "network.retry", mapsTo: "retryEnergy", range: [0, 1] }, { name: "network.strength", mapsTo: "signalStrength", range: [0, 1] }, ], }, reducedMotion: { phase: 0.31, representation: "static broken ring with a diagonal disconnect mark", dataUpdates: "retry-dot", }, labels: { default: "Offline", live: "polite" }, urgency: "medium", }), defineSprite({ id: "transfer.active", category: "workflow", meaning: "uploading, downloading, or synchronizing data", glyph: "transfer", field: "flow", fieldMix: [ layer("flow", 0.46, 1.15, "replace", { speed: 0.88, contrast: 1.14, }), layer("liquid", 0.31, 1.55, "add", { speed: 0.65 }), layer("strata", 0.23, 1.9, "multiply", { speed: 0.44 }), ], palette: "info", threshold: 0.48, density: 0.79, speed: 0.9, pixelShape: "square", pixelSwitch: { mode: "axis-flip", dither: "blue-noise64", hysteresis: 0.052, temporalRate: 0.85, direction: "data-direction", monotonic: false, }, timeline: { mode: "data-or-loop", durationMs: 2200, macroCycleMs: 6600, }, transition: { enter: "axis-flip", exit: "field-morph", durationMs: 380, preservePhase: true, }, interactions: { pointer: { effect: "lateral-wind", strength: 0.07, minSize: 28 }, press: { effect: "spring-compress", strength: 0.045 }, signals: [ { name: "progress", mapsTo: "progress", range: [0, 1], monotonic: true, }, { name: "direction", mapsTo: "direction", values: ["up", "down", "sync"], }, ], }, reducedMotion: { mode: "data-step", phase: 0.5, representation: "static directional arrow with data-driven fill", dataUpdates: "discrete-monotonic", }, labels: { default: "Transferring", live: "polite", value: "{percent}% transferred", variants: { up: "Uploading", down: "Downloading", sync: "Syncing", }, }, }), defineSprite({ id: "workflow.handoff", category: "workflow", meaning: "passing work to another agent or person", glyph: "handoff", field: "flow", fieldMix: [ layer("flow", 0.44, 1.02, "replace", { speed: 0.66, contrast: 1.1, }), layer("constellation", 0.34, 1.6, "screen", { speed: 0.31 }), layer("metaballs", 0.22, 0.85, "add", { speed: 0.38 }), ], palette: "info", threshold: 0.49, density: 0.77, speed: 0.68, pixelShape: "circle", pixelSwitch: { mode: "path-draw", dither: "blue-noise64", hysteresis: 0.055, temporalRate: 0.62, direction: "source-to-destination", }, timeline: { mode: "signal-reactive", durationMs: 3000, macroCycleMs: 6000, replay: "loop-until-accepted", }, transition: { enter: "field-morph", exit: "path-draw", durationMs: 460, preservePhase: true, }, interactions: { pointer: { effect: "bend-path-midpoint", strength: 0.09 }, press: { enabled: false }, signals: [ { name: "handoff.accepted", mapsTo: "accepted", range: [0, 1], latches: true, }, ], }, reducedMotion: { phase: 0.58, representation: "two static nodes joined by a directional arrow", dataUpdates: "destination-highlight", }, labels: { default: "Handing off", live: "polite" }, }), defineSprite({ id: "status.celebration", category: "status", meaning: "a milestone or high-value success", glyph: "celebration", field: "constellation", fieldMix: [ layer("constellation", 0.45, 1.45, "replace", { speed: 0.92, contrast: 1.2, }), layer("caustics", 0.31, 1.9, "screen", { speed: 0.62 }), layer("electric", 0.24, 2.2, "add", { speed: 0.74 }), ], palette: "celebration", threshold: 0.45, density: 0.88, speed: 1, pixelShape: "diamond", pixelSwitch: { mode: "radial-cascade", dither: "blue-noise64", hysteresis: 0.045, temporalRate: 1.1, direction: "bounded-burst", }, timeline: { mode: "one-shot", durationMs: 700, holdMs: 900, replay: "on-enter-once", next: "status.success", }, transition: { enter: "radial-cascade", exit: "field-morph", durationMs: 700, preservePhase: false, }, interactions: { pointer: { effect: "push-fragments", strength: 0.06, minSize: 32 }, press: { enabled: false }, }, reducedMotion: { mode: "static", phase: 1, representation: "static star with a central success mark", transitionMs: 120, }, labels: { default: "Milestone completed", aria: "Milestone completed successfully", live: "polite", }, terminal: true, }), ]; export const SPRITES = deepFreeze( Object.fromEntries(RECIPES.map((recipe) => [recipe.id, recipe])), ); export const SPRITE_IDS = Object.freeze(RECIPES.map((recipe) => recipe.id)); const SPRITE_LIST = Object.freeze([...RECIPES]); export const SPRITE_ALIASES = Object.freeze({ idle: "ai.idle", ready: "ai.idle", ambient: "ai.ambient-idle", "ambient-idle": "ai.ambient-idle", "field-idle": "ai.ambient-idle", "ambient-thinking": "ai.ambient-thinking", "field-thinking": "ai.ambient-thinking", "ambient-thinking-symmetric": "ai.ambient-thinking-symmetric", "field-thinking-symmetric": "ai.ambient-thinking-symmetric", "symmetric-thinking": "ai.ambient-thinking-symmetric", "ambient-speaking": "ai.ambient-speaking", "field-speaking": "ai.ambient-speaking", listening: "ai.listening", thinking: "ai.thinking", "thinking-deep": "ai.thinking-deep", "deep-thinking": "ai.thinking-deep", "still-working": "ai.still-working", working: "ai.still-working", loading: "ai.loading", progress: "ai.progress", generating: "ai.generating", searching: "ai.searching", "tool-use": "ai.tool-use", tool: "ai.tool-use", speaking: "ai.speaking", "awaiting-input": "ai.awaiting-input", prompt: "ai.awaiting-input", success: "status.success", warning: "status.warning", error: "status.error", paused: "status.paused", cancelled: "status.cancelled", offline: "status.offline", transfer: "transfer.active", upload: "transfer.active", download: "transfer.active", sync: "transfer.active", handoff: "workflow.handoff", celebration: "status.celebration", }); export function resolveSpriteId(id) { const normalized = String(id ?? "").trim().toLowerCase(); if (Object.prototype.hasOwnProperty.call(SPRITES, normalized)) { return normalized; } return SPRITE_ALIASES[normalized]; } export function getSprite(id) { const resolved = resolveSpriteId(id); return resolved ? SPRITES[resolved] : undefined; } export function listSprites() { return SPRITE_LIST; } export function hasSprite(id) { return resolveSpriteId(id) !== undefined; } export default SPRITES;