audio/core/AudioOcclusion

Summary: AudioOcclusion.js - Sound Occlusion via Raycasting Casts a ray from each active source to the listener. If the ray intersects occluders (walls, objects), applies lowpass filter + volume attenuation to simulate sound being blocked/muffled. Uses simple AABB intersection for performance. Can be upgraded to use the engine's physics raycasts (PhysX/PBD) for higher fidelity.


createAudioOcclusion

Summary: Create an occlusion system.


addOccluder

addOccluder(occlusion, min, max, transmission = 0.2)

Summary: Add an AABB occluder.


removeOccluder

Summary: Remove an occluder by index.


clearOccluders

Summary: Clear all occluders.


tickOcclusion

tickOcclusion(occlusion, activeSources, listenerPos, context, dt)

Summary: Tick occlusion: check sources against occluders and apply filters.


destroyAudioOcclusion

Summary: Destroy occlusion system.


audio/core/AudioReverbZone

Summary: Create a reverb zone system.


createReverbZoneSystem

Summary: Create a reverb zone system.


addReverbZone

Summary: Add a reverb zone.


removeReverbZone

Summary: Remove a reverb zone.


tickReverbZones

Summary: Tick reverb zones — blend based on listener position.


connectSourceToReverb

connectSourceToReverb(system, source, sendLevel = 0.3)

Summary: Connect a source to the reverb system's convolver (send).


destroyReverbZoneSystem

Summary: Destroy reverb zone system.


audio/core/AudioSource

Summary: AudioSource.js - 3D Sound Emitter Wraps a BufferSourceNode + PannerNode (HRTF) + GainNode chain. Represents a single playing sound in 3D space.


createAudioSource

Summary: Create and start an audio source.


tickAudioSource

Summary: Tick a source (per-frame update).


setSourcePosition

Summary: Update source 3D position explicitly.


setSourceVolume

Summary: Set source volume.


setSourcePitch

Summary: Set source pitch/playback rate.


stopAudioSource

Summary: Stop a source with optional fade-out.


isSourceDone

Summary: Check if source is finished playing.


getSourceEffectiveVolume

Summary: Get effective volume (volume × occlusion).


destroyAudioSource

Summary: Destroy a source and disconnect all nodes.


audio/core/FootstepEvents

Summary: FootstepEvents.js - Footstep Audio Event Registration Registers all footstep and land audio events with the AudioEngine. Called during engine initialization.


registerFootstepEvents

Summary: Register all footstep audio events with the AudioEngine. Should be called once during engine initialization.


unregisterFootstepEvents

Summary: Unregister all footstep events (for cleanup).


audio/core/FootstepMaterials

Summary: FootstepMaterials.js - Footstep Material Taxonomy & Mapping Defines acoustic materials for footsteps (distinct from physics materials) and provides mapping from physics/voxel materials to footstep materials.


resolveFootstepMaterial

Summary: Resolve a physics material ID to a footstep material key.


getFootstepConfig

Summary: Get footstep material config.


getAllFootstepMaterials

Summary: Get all footstep material keys.


Summary: Footstep material configurations. Each defines the procedural patch to use and number of variants.


Summary: Map physics material IDs to footstep materials. Physics materials are from PhysicsAudioBridge.js MATERIAL_TO_SUBSTANCE.


Summary: Map voxel/terrain material strings to footstep materials. Used when querying terrain directly.


audio/core/index

Summary: engine/audio/core/index.js — Barrel export for core audio engine.


audio/index

Summary: engine/audio/index.js — Top-level barrel export for the complete audio system. Exports: Core engine, Spatial pipeline, Bridges, Synth kernel, Sound atoms, Physics models.


audio/synth/BodyFoleyPatches

Summary: Jump effort sound — short vocal grunt (bandpassed noise + formant)


Summary: Slide friction sound — continuous lowpass swept noise


Summary: Cloth rustle — short high-freq noise burst


Summary: Light breathing — soft airy noise with bandpass


Summary: Heavy breathing — louder breath with low-end rumble


audio/synth/FootstepPatches

Summary: FootstepPatches.js - Procedural Footstep Sound Generators Generates material-specific footstep sounds procedurally using Web Audio API. Each patch creates layered sounds (body impact + surface texture) with parameters for speed, weight, and pitch variation.


Summary: Dirt footstep — muffled thud + grain scatter


Summary: Grass footstep — soft rustle + muffled thud


Summary: Sand footstep — soft grain scatter + muffled impact


Summary: Gravel footstep — sharp scatter + rattle


Summary: Mud footstep — wet squelch + suction release


Summary: Snow footstep — soft crunch + squeak


Summary: Stone footstep — hard impact + high-freq crack


Summary: Concrete footstep — dull thud + mid-freq resonance


Summary: Marble footstep — bright click + ring


Summary: Brick footstep — textured impact + dust scatter


Summary: Wood plank footstep — resonant body + plank slap


Summary: Wood creak footstep — creaking friction + resonance


Summary: Metal solid footstep — clang + ring


Summary: Metal grate footstep — rattle + metallic ring


Summary: Water shallow footstep — splash + plop


Summary: Water puddle footstep — small splash


Summary: Leaves footstep — crunch + rustle


Summary: Glass footstep — delicate clink + shimmer


Summary: Ice footstep — crack + slide


Summary: Generic land sound — reuses footstep patch but with heavier weight/volume Can be used as a wrapper for any footstep material


getFootstepPatch

Summary: Get footstep patch function by material key.


audio/synth/NodeRegistry

Summary: NodeRegistry.js — Unified Node Type Registry Single source of truth for mapping node type names to their Web Audio factory functions and worklet processor names. Usage: import { getNodeFactory, getRegisteredTypes } from './NodeRegistry.js'; const factory = getNodeFactory('Waveguide'); const node = factory(ctx, params, pitchRatio);


getNodeFactory

Summary: Get the Web Audio factory function for a node type. Returns a function: (ctx, nodeDefinition, pitchRatio) => { output, input, sources }


getRegisteredTypes

Summary: Get all registered type names.


getNodeCategory

Summary: Get the category for a node type.


hasWorkletProcessor

Summary: Check if a type has a worklet processor implementation.


Summary: Node category definitions for organization/UI.


Summary: All registered node type names (flat list).


audio/synth/PatchCompiler

Summary: PatchCompiler.js - Compile Patch JSON → Execution Graph Parses a patch JSON descriptor, validates nodes and wires, performs topological sort, detects feedback loops, and outputs a compiled execution plan for PatchRunner.worklet.js.


registerNodeType

Summary: Register a node type for the compiler.


getNodeTypes

Summary: Get all registered node types.


compilePatch

Summary: Compile a patch JSON into an execution plan.


validatePatch

Summary: Validate a patch without fully compiling.


Summary: PatchRunner.worklet.js - AudioWorkletProcessor Executes a compiled patch graph at sample rate (128 samples/block). Receives compiled plans via MessagePort, reads game params from SharedArrayBuffer, and outputs synthesized audio. Register with: audioContext.audioWorklet.addModule('PatchRunner.worklet.js')


audio/synth/ProceduralPatches

Summary: ProceduralPatches.js - Real-time procedural sound patches for particle substances Each patch is a factory: (ctx, destination, params) => { stop(), update?(params) } ctx         — AudioContext destination — AudioNode (pre-spatialized via PannerNode) params      — { volume, pitch, speed, temperature, ... } Impact patches are one-shot (auto-stop). Ambient patches loop until stop() is called, and support update() for real-time modulation.


Summary: Water/fluid impact — velocity-driven splat/drip/splash. Sound design based on real water acoustics: - "Plop" body: fast sine pitch-sweep downward (Helmholtz cavity resonance). A water droplet traps an air bubble on impact; the bubble oscillates at a frequency that sweeps down as it grows. Duration ~15-40ms. - "Spray" transient: ultra-short highpass noise burst (<20ms) for the broadband crack of water hitting a surface. Only audible at higher speeds. - Bubble resonances: at high speed, 1-3 random short sine pips simulate secondary bubbles breaking the surface. Speed mapping: slow  (< 1)  → gentle drip: mostly plop body, minimal spray medium (1-4)  → splat: plop + moderate spray fast  (> 4)  → full splash: plop + loud spray + bubble pips


Summary: Metal clang — multi-partial ring + noise transient


Summary: Glass clink — high FM harmonics, crystalline


Summary: Stone/debris thud — low filtered noise + crack + sub thump


Summary: Wood knock — multi-mode resonant body + knock transient


Summary: Ice crack — sharp noise + crystalline inharmonic pings


Summary: Fire/lava impact burst — noise burst + low boom + rumble tail


Summary: Sand soft impact — muffled body + grain scatter


Summary: Viscous liquid drop/drip — thicker plop than water, low resonance, no spray


Summary: Gas/vapor/granular hiss — soft broadband whoosh, not a hard impact


Summary: Spark/debris crackle — scattered hard micro-transients


Summary: Plasma/spark electric snap


Summary: Fire/lava crackling ambient — random noise bursts + low rumble


Summary: Viscous/fluid flow ambient — subtle high-freq trickle. Shared by water + viscous liquids through AMBIENT_PATCHES aliases.


Summary: Plasma/electric buzz ambient — FM buzz + noise crackle


Summary: Smoke/steam/wind ambient — airy bandpass sweep


getImpactPatch

Summary: Get the impact patch function for a substance key. If runtime overrides exist for this key, returns a wrapper that merges them.


getAmbientPatch

Summary: Get the ambient patch function for a substance key. If runtime overrides exist for this key, returns a wrapper that merges them.


setPatchOverride

Summary: Set a param override for a material patch. These overrides are merged into patch params at call time.


setPatchOverrides

Summary: Set all overrides for a material at once.


getPatchOverride

Summary: Get current overrides for a material.


clearPatchOverrides

Summary: Clear all runtime overrides (reset to defaults).


getAllPatchKeys

Summary: Get all unique canonical material keys (de-duplicated patch function names). Returns objects with { key, type, patchFn } for both impact and ambient.


getAllMaterialAliases

Summary: Get a flat list of all material alias keys (including duplicates).


Summary: Map of proceduralPatch strings → impact patch functions


Summary: Map of proceduralPatch strings → ambient patch functions


audio/synth/SfxGenerator

Summary: SfxGenerator.js — Parametric Sound Effect Generator sfxr-inspired procedural SFX system. Every sound is defined by a compact parameter object — no samples needed. Renders to AudioBuffer or plays directly via Web Audio. Parameters cover: waveform, envelope, frequency sweep, vibrato, arpeggiation, duty cycle, retrigger, flanger, LP/HP filter, gain. Preset categories with randomize/mutate for instant sound creation: Pickup, Laser, Explosion, Powerup, Hit, Jump, Blip, Click, Error, Coin Usage: import { createSfxParams, randomizePreset, mutateSfx, renderSfx, playSfx } from './SfxGenerator.js'; const params = randomizePreset('laser'); const buffer = renderSfx(audioCtx, params); playSfx(audioCtx, buffer);


createSfxParams

Summary: Create a default SFX parameter set. All values normalized to sensible ranges.


randomizePreset

Summary: Generate a randomized SFX from a preset category.


randomizeFull

Summary: Fully random SFX (no preset bias).


mutateSfx

Summary: Mutate an existing SFX params slightly (small random nudge to each param).


getPresetNames

Summary: Get list of available preset names.


renderSfx

Summary: Render an SFX to an AudioBuffer. This is a pure software synthesizer — runs offline, produces a buffer.


playSfx

Summary: Play a rendered SFX buffer.


renderAndPlay

renderAndPlay(ctx, params, options = {})

Summary: Render and immediately play an SFX.


encodeSfxParams

Summary: Encode SFX params to a compact JSON string.


decodeSfxParams

Summary: Decode SFX params from a JSON string.


audio/synth/SharedParamBuffer

Summary: SharedParamBuffer.js - Game → AudioWorklet Parameter Streaming Allocates a SharedArrayBuffer for zero-copy, zero-latency parameter updates from the main game thread to the AudioWorklet synthesis thread. Layout: Float32Array where each slot maps to an exposed synth parameter. The main thread writes emitter aggregate stats each frame; the worklet reads them.


createSharedParamBuffer

Summary: Create a shared parameter buffer.


writeEmitterStats

Summary: Write emitter aggregate stats to the shared buffer.


clearAllEmitterStats

Summary: Clear all emitter stats (e.g. when emitters stop).


clearEmitterStats

Summary: Clear stats for a specific emitter.


readEmitterParam

Summary: Read a single param value from the shared buffer.


Summary: Standard parameter indices for emitter → synth streaming. Each active emitter gets one set of these slots.


audio/synth/SoundBlender

Summary: SoundBlender.js — Multi-Layer Sound Effect Mixer Blends 2-4 sound sources (AudioBuffers) with per-layer controls: - Volume, pitch shift, delay offset - Crossfade curves (linear, equal-power, cosine) - Random variation ranges per parameter - Trigger modes: simultaneous, sequential, random-pick, round-robin Produces infinite unique variations from a small set of source sounds. Usage: import { createBlendPreset, renderBlend, playBlend } from './SoundBlender.js'; const preset = createBlendPreset([buffer1, buffer2], { layers: [ { volume: 0.8, pitch: 1.0, delay: 0, volumeRandom: 0.1, pitchRandom: 0.05 }, { volume: 0.5, pitch: 1.2, delay: 0.05, volumeRandom: 0.2, pitchRandom: 0.1 } ], mode: 'simultaneous' }); playBlend(ctx, preset);


createBlendPreset

createBlendPreset(buffers = [], config = {})

Summary: Create a blend preset from source buffers and layer config.


addLayer

addLayer(preset, buffer, layerConfig = {})

Summary: Add a layer to an existing preset.


removeLayer

Summary: Remove a layer by index.


playBlend

Summary: Play a blend preset. Returns an object with stop() method.


renderBlend

Summary: Render a blend preset to a single AudioBuffer (offline).


generateVariations

async generateVariations(ctx, preset, count = 10)

Summary: Generate N unique variations of a blend preset. Each variation resolves random ranges differently.


audio/synth/SoundSchema

Summary: SoundSchema.js — JSON Schema Definitions for All Sound Types Each schema defines every parameter with: type, min, max, step, default, label, description, unit, group Used by: - AudioNodeInspector: auto-generate UI controls from schema - VariationGenerator: derive ranges/limits automatically - Validation: validateParams() catches bad data on import/paste - Sound Palette: self-describing serialized entries Schema field types: 'float'   → slider (min/max/step) 'int'     → integer slider or number input 'enum'    → dropdown (options array) 'bool'    → toggle 'string'  → text input 'array'   → special handling per context


getSchema

Summary: Get a schema by type name.


getSchemaNames

Summary: Get all schema names.


validateParams

Summary: Validate a params object against a schema. Returns { valid, errors, sanitized }. - sanitized: a clean copy with missing keys filled from defaults and out-of-range values clamped. - errors: array of { key, message } for any issues found.


defaultsFromSchema

Summary: Create default params from a schema.


variationRangesFromSchema

variationRangesFromSchema(schema, proportion = 0.1)

Summary: Extract variation ranges from a schema (for VariationGenerator). Returns { ranges, limits } where: ranges[key] = (max - min) * proportion limits[key] = [min, max]


getParamGroups

Summary: Get parameter groups from a schema (for UI grouping).


audio/synth/SynthManager

Summary: SynthManager.js - Main-Thread Synthesis Orchestrator Manages AudioWorklet registration, patch instance lifecycle, parameter updates via MessagePort and SharedArrayBuffer.


createSynthManager

Summary: Create a synth manager.


initSynthManager

Summary: Initialize the synth manager — register the AudioWorklet.


registerPatch

Summary: Register a patch in the library (compile and cache).


getPatch

Summary: Get a compiled patch plan.


createPatchInstance

createPatchInstance(manager, patchId, options = {})

Summary: Create and start a patch instance (a playing synth voice).


setInstanceParam

setInstanceParam(manager, instanceId, paramName, value)

Summary: Set an exposed parameter on a running instance.


setInstanceVolume

Summary: Set volume on a running instance.


destroyPatchInstance

destroyPatchInstance(manager, instanceId, fadeOut = 0.05)

Summary: Stop and destroy a patch instance.


destroyAllInstances

Summary: Stop all instances.


destroySynthManager

Summary: Destroy the synth manager.


audio/synth/VariationGenerator

Summary: VariationGenerator.js — Automatic Sound Variation System Takes any parametric sound definition and generates N unique variations by randomizing parameters within user-defined ranges. Works with: - SfxGenerator params - FM synthesis configs - Karplus-Strong configs - Any flat parameter object Also provides batch rendering to AudioBuffer arrays and a sound palette system for saving/loading favorites. Usage: import { createVariationConfig, generateVariations, renderVariationBatch } from './VariationGenerator.js'; const vc = createVariationConfig(baseParams, { startFrequency: 0.1, decayTime: 0.05 }); const variations = generateVariations(vc, 20); const buffers = await renderVariationBatch(ctx, variations, renderFn);


createVariationConfig

createVariationConfig(baseParams, ranges = {}, options = {})

Summary: Create a variation configuration.


createVariationConfigFromSchema

createVariationConfigFromSchema(baseParams, schema, proportion = 0.1, options = {})

Summary: Create a variation config from a SoundSchema. Auto-derives ranges and limits from the schema definition.


lockParam

Summary: Lock a parameter so it won't be varied.


unlockParam

Summary: Unlock a parameter for variation.


setRange

Summary: Set the random range for a specific parameter.


setLimits

Summary: Set per-key min/max limits.


generateVariations

Summary: Generate N parameter variations from a variation config.


generateOneVariation

Summary: Generate a single variation.


renderVariationBatch

Summary: Render an array of parameter variations to AudioBuffers.


renderVariationBatchAsync

async renderVariationBatchAsync(ctx, paramsList, renderFn, onProgress = null)

Summary: Render variations async (yields between each to avoid blocking).


writeSoundPaletteRecord

writeSoundPaletteRecord(palette, storage = globalThis.localStorage)


createSoundPalette

Summary: Create a sound palette for organizing favorite sounds.


addToPalette

Summary: Add a sound to the palette.


removeFromPalette

Summary: Remove a sound from the palette.


searchPalette

Summary: Find sounds in the palette by category or tag.


savePalette

savePalette(palette, storage = globalThis.localStorage)

Summary: Save palette to localStorage.


loadPalette

Summary: Load palette from localStorage.


exportPalette

Summary: Export palette as JSON string (for sharing/backup).


importPalette

Summary: Import palette from JSON string.


audio/synth/WebAudioNodeFactory

Summary: WebAudioNodeFactory.js — Unified Web Audio Node Factory Central factory for creating main-thread Web Audio nodes from patch graph node definitions. Delegates to modular node files where possible. Used by AudioEditorPanel.js for preview playback. Usage: import { createWebAudioNode, buildVoice } from './WebAudioNodeFactory.js'; const node = createWebAudioNode(ctx, { type: 'Waveguide', params: {...} }, pitchRatio); const voice = buildVoice(ctx, patchJSON, pitchRatio);


normalizeWaveShape

Summary: Normalize oscillator wave shape names to Web Audio API values.


buildVoice

buildVoice(ctx, patch, pitchRatio = 1.0)

Summary: Build a complete voice from a patch graph, scaling frequencies by pitchRatio.


createWebAudioNode

createWebAudioNode(ctx, node, pitchRatio = 1.0)

Summary: Create a Web Audio node (or subgraph) for a given patch graph node definition. Returns { output, input, inputMap?, sources } or null.


audio/synth/index

Summary: engine/audio/synth/index.js — Barrel export for procedural synthesis engine.


audio/synth/nodes/FMOperatorNode

Summary: FMOperatorNode.js — FM Synthesis Engine Frequency Modulation synthesis: one oscillator (modulator) modulates the frequency of another (carrier), producing complex harmonic and inharmonic timbres from simple parameters. Supports up to 4 operators in series/parallel configurations. Each operator has: frequency ratio, detune, envelope, feedback. Usage: import { renderFM, playFM, createFMPatch } from './FMOperatorNode.js'; const buffer = renderFM(audioCtx, { carrierFreq: 440, modRatio: 2, modDepth: 200 });


renderFM

Summary: Render 2-operator FM synthesis to an AudioBuffer.


Summary: Render a 4-operator FM stack. Operators are chained: op4 → op3 → op2 → op1 (carrier).


playFM

playFM(ctx, config = {}, destination = null)

Summary: Play FM synthesis directly.


getFMPresetNames

Summary: Get list of FM preset names.


audio/synth/nodes/FormantFilterNode

Summary: FormantFilterNode.js — Formant Filter for Vocal/Creature Sounds Parallel bank of bandpass filters tuned to vocal formant frequencies. Shapes any input signal into vowel-like resonances. Useful for: - Creature vocalizations - Vocal pads and textures - Talking/singing synthesis - Resonant environmental effects Includes presets for standard vowels (A, E, I, O, U) and creature sounds. Usage: import { createFormantFilter, setFormantVowel, destroyFormantFilter } from './FormantFilterNode.js'; const filter = createFormantFilter(audioCtx); sourceNode.connect(filter.input); filter.output.connect(audioCtx.destination); setFormantVowel(filter, 'A');


createFormantFilter

Summary: Create a formant filter bank.


setFormantVowel

Summary: Set the formant filter to a vowel preset.


setFormantFrequencies

setFormantFrequencies(instance, frequencies, bandwidths, gains)

Summary: Set formant frequencies directly (bypass presets).


morphFormants

morphFormants(instance, vowelA, vowelB, blend)

Summary: Morph between two vowel presets.


setFormantMorphTime

Summary: Set morph time (transition speed).


destroyFormantFilter

Summary: Destroy the formant filter and release resources.


getFormantPresetNames

Summary: Get list of available formant preset names.


audio/synth/nodes/GranularNode

Summary: Create a granular synthesis engine.


createGranularEngine

createGranularEngine(ctx, buffer, config = {})

Summary: Create a granular synthesis engine.


startGranular

Summary: Start the granular engine — begins spawning grains.


stopGranular

Summary: Stop the granular engine.


setGranularParam

Summary: Set a granular parameter.


setGranularBuffer

Summary: Replace the source buffer.


connectGranular

Summary: Connect granular output to a destination.


destroyGranular

Summary: Destroy and clean up.


audio/synth/nodes/ImpulseNode

Summary: Create a real-time impulse generator engine.


generateImpulseBuffer

Summary: Generate a single impulse as an AudioBuffer.


audio/synth/nodes/KarplusStrongNode

Summary: Render a Karplus-Strong plucked string to an AudioBuffer.


playKarplusStrong

playKarplusStrong(ctx, config = {}, destination = null)

Summary: Play a Karplus-Strong sound directly.


audio/synth/nodes/MidiInputNode

Summary: MidiInputNode.js — MIDI Input Integration Receives MIDI input from external controllers or DAW via Web MIDI API. Outputs gate, note, velocity, and CC signals for driving synth nodes. Usage: import { createMidiInput, requestMidiAccess } from './MidiInputNode.js'; await requestMidiAccess(); const midi = createMidiInput({ channel: 0 }); midi.onNoteOn = (note, velocity) => synth.noteOn(note, velocity);


requestMidiAccess

Summary: Request MIDI access from the browser.


getMidiInputs

Summary: Get list of available MIDI inputs.


createMidiInput

Summary: Create a MIDI input node that processes incoming MIDI messages.


createMidiCCMap

Summary: Create a CC mapper that scales CC values to a target range.


audio/synth/nodes/ModalBankNode

Summary: ModalBankNode.js — Modal Synthesis Engine Physical modeling using parallel resonant filters (modes). Each mode has frequency, amplitude, and decay characteristics. Material presets auto-calculate modes based on: - Stiff string formula (metal, piano strings) - Beam formula (wood, glass bars) - Circular membrane formula (drums) - Rectangular plate formula (bells, gongs) Usage: import { createModalBank, MODAL_MATERIALS } from './ModalBankNode.js'; const bank = createModalBank(audioCtx, { material: 'metal', frequency: 440 }); bank.excite(impulseBuffer);


calculateModeFrequencies

calculateModeFrequencies(formula, f0, modeCount, inharmonicity = 0)

Summary: Calculate modal frequencies based on material formula.


calculateModeAmplitudes

calculateModeAmplitudes(modeCount, strikePosition, brightness = 0.5)

Summary: Calculate mode amplitudes based on strike position.


calculateModeDecays

calculateModeDecays(frequencies, baseDamping, highDamp, f0)

Summary: Calculate mode decay times based on frequency and damping.


createModalBank

Summary: Create a modal bank synthesis engine.


audio/synth/nodes/NoiseGeneratorNode

Summary: Create a noise AudioBuffer of the specified color.


createNoiseBuffer

createNoiseBuffer(ctx, color = 'white', duration = DEFAULT_DURATION, options = {})


createNoiseSource

createNoiseSource(ctx, color = 'white', options = {})

Summary: Create a looping noise source node.


createStereoNoiseSource

createStereoNoiseSource(ctx, color = 'white', options = {})

Summary: Create stereo noise with decorrelated channels.


audio/synth/nodes/OscillatorNode

Summary: OscillatorNode.js — Advanced Oscillator Synthesis Supports multiple waveform types beyond Web Audio defaults: - sine, square, sawtooth, triangle (native) - pulse (variable duty cycle via PeriodicWave) - supersaw (multiple detuned sawtooths) - custom (user-defined wavetable) Additional features: - Sub-oscillator (-1 or -2 octaves) - Unison/supersaw with configurable voice count and spread - Pulse width modulation Usage: import { createOscillator, createSupersawOscillator } from './OscillatorNode.js'; const osc = createOscillator(audioCtx, { shape: 'pulse', frequency: 440, pulseWidth: 0.3 }); osc.output.connect(destination); osc.start();


createOscillator

Summary: Create an advanced oscillator node.


audio/synth/nodes/SamplePlayerNode

Summary: SamplePlayerNode.js — Advanced Sample Playback Enhanced sample player with: - Loop modes: forward, pingpong, reverse - Start/end points and loop regions - Crossfade looping for seamless loops - Built-in ADSR envelope - Pitch tracking from MIDI note Usage: import { createSamplePlayer } from './SamplePlayerNode.js'; const player = createSamplePlayer(audioCtx, buffer, { loop: true, loopMode: 'pingpong' }); player.trigger();


createSamplePlayer

createSamplePlayer(ctx, buffer, config = {})

Summary: Create an advanced sample player.


createCrossfadeLoopBuffer

createCrossfadeLoopBuffer(ctx, buffer, loopStart, loopEnd, crossfadeDuration)

Summary: Create a crossfade loop buffer. Blends the end of the loop region back into the start for seamless looping.


audio/synth/nodes/SequencerNode

Summary: Create a master clock with BPM and time signature.


createStepSequencer

Summary: Create a step sequencer.


createArpeggiator

Summary: Create an arpeggiator.


audio/synth/nodes/SynthesisTypes

Summary: SynthesisTypes.js — Additional Synthesis Methods Extended synthesis techniques: - Additive: Harmonic/partial-based synthesis - Wavetable: Morphing wavetable oscillator - PhaseDistortion: CZ-style phase distortion - Subtractive: Classic subtractive synth voice Usage: import { createAdditiveSynth, createWavetableSynth } from './SynthesisTypes.js'; const add = createAdditiveSynth(audioCtx, { partialCount: 32 });


createAdditiveSynth

Summary: Create an additive synthesizer.


createWavetableSynth

Summary: Create a wavetable synthesizer.


createPhaseDistortionSynth

createPhaseDistortionSynth(ctx, config = {})

Summary: Create a phase distortion synthesizer (CZ-style).


createSubtractiveSynth

createSubtractiveSynth(ctx, config = {})

Summary: Create a classic subtractive synthesizer voice.


audio/synth/nodes/WaveguideNode

Summary: Render a waveguide to an AudioBuffer (offline).


createWaveguide

Summary: Create a real-time waveguide engine using Web Audio nodes. This is a simplified version using delay + filter + feedback.


audio/synth/nodes/atoms/CrackleAtom

Summary: CrackleAtom.js - Crackle Sound Atom (Verron 2012) Pre-wired patch: Impulse → ModalBank (2-3 modes) → Envelope (AD) → Gain Driven by: emitRate → density, temperature → brightness Use case: fire crackle, ice crack, sparks


audio/synth/nodes/atoms/DropAtom

Summary: DropAtom.js - Drop Sound Atom (Verron 2012) Pre-wired patch: Impulse → Resonator (single mode) → Envelope (AD) Driven by: mass → pitch, velocity → amplitude Use case: rain drops, drips, liquid impacts


audio/synth/nodes/atoms/HissAtom

Summary: HissAtom.js - Hiss Sound Atom (Verron 2012) Pre-wired patch: Noise(white) → HighPass → Gain Driven by: temperature → cutoff, emitRate → volume Use case: steam, gas escape, sizzle, continuous background


audio/synth/nodes/atoms/RumbleAtom

Summary: RumbleAtom.js - Rumble Sound Atom (Verron 2012) Pre-wired patch: BrownNoise → LowPass → Gain Driven by: size → cutoff, velocity → volume Use case: thunder, ground shake, lava, explosions


audio/synth/nodes/atoms/WhooshAtom

Summary: WhooshAtom.js - Whoosh Sound Atom (Verron 2012) Pre-wired patch: Noise → BandPass → Envelope → Gain Driven by: velocity → centerFreq, size → bandwidth Use case: wind, projectile flyby, movement


audio/synth/nodes/instruments/InstrumentPresets

Summary: Convert MIDI note number to frequency.


midiToFreq

Summary: Convert MIDI note number to frequency.


freqToMidi

Summary: Convert frequency to MIDI note number.


createPianoInstrument

Summary: Create a piano instrument using Karplus-Strong + FM.


createStringsInstrument

createStringsInstrument(ctx, config = {})

Summary: Create a string ensemble instrument.


createBrassInstrument

Summary: Create a brass instrument with growl and brightness.


createPadSynth

Summary: Create a lush pad synthesizer.


createLeadSynth

Summary: Create a mono lead synthesizer with glide.


createBassSynth

Summary: Create a bass synthesizer.


createDrumSynth

Summary: Create an analog-style drum synthesizer.


audio/synth/nodes/physics/CombustionModel

Summary: CombustionModel.js - Fire/Combustion Synthesis Model Combines Crackle + Hiss + Rumble atoms mixed by intensity. Low intensity = gentle crackle. High intensity = roaring fire with rumble. Driven by: emitRate, temperature


audio/synth/nodes/physics/FluidModel

Summary: FluidModel.js - Fluid/Water Synthesis Model Combines Drop atoms (splashes) + Hiss (flow) for liquid sounds. Driven by: emitRate, velocity, splash rate


audio/synth/nodes/physics/ModalImpactModel

Summary: ModalImpactModel.js - Material-Aware Impact Synthesis Excites a bank of resonators on collision. Material properties determine mode frequencies, Q factors, and decay times. Uses MATERIAL enum from SubstanceSchema for lookup. Based on Cook/Smith modal synthesis (Stanford CCRMA).


getModalPreset

Summary: Get modal preset for a material.


getModalMaterials

Summary: Get all available material names.


createModalImpactPatch

Summary: Generate a ModalImpact patch descriptor for a given material.


audio/synth/nodes/physics/WeatherModel

Summary: WeatherModel.js - Weather Synthesis Model Rain (grain cloud of drops), hail, thunder (rumble). Driven by: weather type, intensity


audio/synth/nodes/physics/WindModel

Summary: WindModel.js - Wind Synthesis Model Combines Whoosh + Rumble, with gust modulation from WindSystem. Driven by: windSpeed, gustiness


collab/CollabAnonymousRelay

Summary: CollabAnonymousRelay.js Anonymous peer mapping and routing for P2P collab channels. Inspired by Tor onion routing, I2P garlic routing, and AP3 pseudonymous overlays — adapted for real-time game networking where latency matters. Three layers of anonymity: 1. Pseudonymous IDs — each peer gets a random session token (anonId). All ops use anonIds instead of real selfIds. Peers never see each other's real identity. Different anonId each session prevents cross-session correlation. 2. TURN-only relay — forces iceTransportPolicy:'relay' so WebRTC ICE candidates never leak real IP addresses between peers. 3. Garlic encryption — each message payload is encrypted with the recipient's ECDH public key before being sent through the relay. The relay host can route messages (via anonId headers) but cannot read the encrypted payload. Only the intended recipient can decrypt. Architecture: - The host acts as an anonymizing relay (already in relay mode) - Peers send messages to the host with a destination anonId - The host maps anonId → realPeerId, forwards the message - The forwarded message has the sender's anonId (not real ID) - Result: no peer knows any other peer's real identity or IP Flow: 1. createAnonymousRelay() → relay state with self anonId 2. Host: registerPeer(realPeerId) → assigns anonId, stores mapping 3. Outgoing: sanitizeOp(op) → replaces peerId with anonId 4. Host routing: resolveAnonToReal(anonId) → realPeerId for delivery 5. Incoming: the op already has sender's anonId (safe to process) 6. Garlic: wrapGarlic(op, recipientEcdhPub) → encrypted payload 7. Garlic: unwrapGarlic(op, ownEcdhPrivate) → decrypted payload


collabAnonTimestampReport

collabAnonTimestampReport(timestamp, nowMs = Date.now()


collabAnonScoreReport

collabAnonScoreReport(scoreData, nowMs = Date.now()


getAnonUsername

Summary: Generate an anonymous username from an anonId. Format: "Anon-XXXX" where XXXX is first 4 chars of the anonId.


createAnonymousRelay

Summary: Create an anonymous relay state.


registerAnonPeer

Summary: Register a peer and assign them an anonymous ID. On the host: stores both directions of the mapping. On non-host: only stores our own mapping (we know our own anonId).


removeAnonPeer

Summary: Remove a peer from the anonymous mapping.


resolveRealToAnon

Summary: Resolve a real peer ID to its anonymous ID.


resolveAnonToReal

Summary: Resolve an anonymous ID back to the real peer ID. HOST ONLY — non-host peers cannot resolve other peers' anonIds.


resolveAnonUsername

Summary: Get the anonymous username for a peer.


sanitizeOutgoingOp

Summary: Sanitize an outgoing op: replace all real peer IDs with anonymous IDs. This is called before broadcasting — peers only see anonIds.


deanonymizeIncomingOp

Summary: De-anonymize an incoming op: resolve anonIds back to real peer IDs. HOST ONLY — used for internal routing decisions. The forwarded copy to other peers keeps the anonIds intact.


reanonymizeForForward

Summary: Re-anonymize an op for forwarding to another peer. The host de-anonymizes for routing, then this re-anonymizes for delivery. This ensures the recipient only sees the sender's anonId.


wrapGarlic

async wrapGarlic(relay, op, recipientAnonId, ownEcdhPrivateKey)

Summary: Wrap an op in garlic encryption for a specific recipient. The payload is encrypted with a shared key derived from our ECDH private key + the recipient's ECDH public key. The relay host can route (via 'to' field) but cannot read the encrypted payload.


unwrapGarlic

async unwrapGarlic(relay, garlicOp, ownEcdhPrivateKey)

Summary: Unwrap a garlic-encrypted message using our ECDH private key.


buildAnonHello

Summary: Build an anonymous hello payload. Replaces the real selfId/username with anonymous versions. The ECDH public key is still included (needed for garlic encryption) but it's an ephemeral session key, not tied to persistent identity.


processAnonHello

Summary: Process an anonymous hello on the host side. The host needs to map the anonymous selfId to the real peer connection.


getAnonIceConfig

Summary: Get ICE configuration for anonymous mode. Forces TURN-only relay to prevent IP address leakage.


sanitizePresence

Summary: Check if a peer list entry should be visible to other peers. In anonymous mode, strip real identifiers from presence data.


getAnonRelayStats

Summary: Get anonymous relay stats for UI display.


isAnonEnabled

Summary: Check if anonymous mode is active on a relay.


generateSessionSeed

Summary: Generate a session seed (host only). Shared with all peers so they can build their credential proof.


generateAnonSigningKey

Summary: Generate a fresh session ECDSA P-256 key pair for anonymous signing. This key is ephemeral — different every session, never persisted.


buildAnonCredential

Summary: Build an anonymous credential: prove we're a legitimate peer by signing the sessionSeed with our persistent ECDSA key. The credential proves: "I possess the private key for a registered identity" without revealing WHICH identity (the credential is sent only to the host via E2E encrypted channel, and the host discards the mapping after verification).


verifyAnonCredential

async verifyAnonCredential(persistentPubKeyRaw, sessionSeed, credential)

Summary: Verify an anonymous credential (HOST ONLY). Confirms the credential was signed by the persistent key we know for this peer.


signAnonScoreReceipt

Summary: Sign a score receipt with the anonymous session key. Uses the same canonical format as signScoreReceipt but with the session key. The anonId is used instead of the real selfId in the signed data.


verifyAnonScoreReceipt

async verifyAnonScoreReceipt(certifiedSessionKeys, scoreData, signature, signerPublicKey = null, nowMs = Date.now()

Summary: Verify an anonymous score receipt against the list of certified session keys. Returns true if ANY certified session key validates the signature. This is the key anonymity property: verifier confirms the receipt is from a legitimate peer without learning WHICH peer.


signAnonCriticalOp

async signAnonCriticalOp(sessionKeyPair, sessionPubKeyRaw, op, anonId)

Summary: Sign a critical op with the anonymous session key. Same canonical format as signCriticalOp but uses session key + anonId.


verifyAnonCriticalOp

async verifyAnonCriticalOp(op, senderAnonId, certifiedSessionKeys, relay = null, nowMs = Date.now()

Summary: Verify a critical op signed with an anonymous session key. Checks the signature against ALL certified session keys.


initAnonCredentials

Summary: Initialize the anonymous credential state on the relay. Called after the relay is created and the host generates a session seed.


certifySessionKey

Summary: Add a verified session public key to the certified list (HOST). Called after the host verifies a peer's anonymous credential.


setCertifiedSessionKeys

Summary: Set the full certified session key list (NON-HOST peers). Called when the host broadcasts the shuffled list of all certified keys.


getCertifiedSessionKeys

Summary: Get the list of certified session keys.


buildAnonCredentialOp

Summary: Build an __anon_credential__ op for the host. Sent after joining to prove legitimacy and register session key.


buildAnonKeylistOp

Summary: Build an __anon_keylist__ op to broadcast the certified session key list. HOST ONLY — sent to all peers after verifying credentials.


buildAnonScoreReportOp

async buildAnonScoreReportOp(relay, targetAnonId, score)

Summary: Build an anonymous score report op. Uses the session key for signing instead of the persistent ECDSA key.


isAnonCredentialsReady

Summary: Check if anonymous credentials are ready for signing/verification.


collab/CollabCameraInterp

Summary: CollabCameraInterp.js Hermite-spline camera interpolation for smooth remote peer camera rendering. Based on Glenn Fiedler's "Snapshot Interpolation" (Gaffer on Games, GDC 2015): - Buffer camera snapshots and render slightly behind real-time - Use cubic Hermite splines (position + velocity at each sample) for C1 continuity - Adaptive jitter compensation adjusts the playback delay automatically Hermite basis: H(t) = (2t³ - 3t² + 1)·p0  +  (t³ - 2t² + t)·v0·Δt + (-2t³ + 3t²)·p1      +  (t³ - t²)·v1·Δt This guarantees the curve passes through p0 and p1 with matching velocities, eliminating the 1st-order discontinuity artifacts that linear interpolation causes.


createCameraInterpState

Summary: Create the global camera interpolation state. Attach to editor.collab or pass around explicitly.


pushCameraSnapshot

pushCameraSnapshot(state, peerId, pos, target, vel, targetVel, remoteTs)

Summary: Push a camera_sync snapshot into the interpolation buffer. Call this from the camera_sync op handler instead of snapping peer.camera directly.


getInterpolatedCamera

Summary: Get the smoothly interpolated camera position for a remote peer. Call this every render frame from CollabOverlayRenderer.


pruneStaleInterpBuffers

Summary: Remove stale peer buffers. Call periodically (~1Hz).


removePeerInterpBuffer

Summary: Remove a specific peer's buffer (on disconnect).


getInterpStats

Summary: Get interpolation stats for UI display.


destroyCameraInterpState

Summary: Destroy all state.


collab/CollabCodec

Summary: CollabCodec.js Inline MessagePack encoder/decoder for binary collab messages. Zero dependencies — implements msgpack spec directly. Supports: null, bool, int (safe integer range), float64, string, bin (Uint8Array), array, map (plain objects). Special extension type 0x01: Float32Array with canonical little-endian bytes. Usage: import { encode, decode } from './CollabCodec.js'; const bytes = encode({ ch: 'ops', data: { type: 'transform', pos: new Float32Array([1,2,3]) } }); const obj = decode(bytes);


encode

Summary: Encode a JS value into a msgpack Uint8Array.


decode

Summary: Decode a msgpack Uint8Array (or ArrayBuffer) back into a JS value.


collab/CollabCore

Summary: CollabCore.js WebRTC peer connection management. Manages RTCPeerConnection instances, data channels, and reconnection. Two data channels per peer: 'ops'      — reliable, ordered (scene operations) 'presence' — unreliable, unordered (camera/selection at 10Hz)


createCollabCore

Summary: Create a collab core instance. config: { projectId, selfId, username, onOp, onPresence, onPeerDiscovered, shouldConnectPeer, onPeerJoin, onPeerLeave }


startCollab

Summary: Start signaling and begin peer discovery.


forceDiscovery

Summary: Force an immediate discovery pass: re-announce on all signaling tiers right now. Lets an app-layer "Connect now" find a known contact without waiting for the periodic announce cycle. Does not tear down existing peers.


broadcastOp

Summary: Broadcast an op to all connected peers. Transport priority: 1. FastChannel (same-origin BroadcastChannel) — <1ms 2. Gossip mesh (partial mesh / supernode mode) — K neighbors, O(log N) delivery 3. Full mesh WebRTC — direct to all (≤6 peers)


broadcastPresence

Summary: Broadcast presence data to all connected peers (unreliable channel). Prefers fast channel for same-origin peers.


sendToPeer

Summary: Send data to a specific peer only. Prefers fast channel for same-origin peers. Auto-chunks large messages to avoid SCTP size limits.


relayOp

Summary: Relay an op from one peer to all other peers (host-only, relay mode). Optionally filters by spatial interest set.


computeDynamicCapacity

Summary: Compute the dynamic peer capacity based on connection quality. Fast channel peers are free. Trusted peers expand the mesh limit.


checkRelayMode

Summary: Check peer count and toggle relay mode if threshold is crossed. Now uses dynamic capacity — fast peers don't count, trusted peers expand the limit.


setCoreRelayConfig

Summary: Set the spatial authority reference for interest-filtered relay.


setCoreFastChannel

Summary: Set the fast channel reference on the core. Called by EditorCollab after creating the fast channel.


setCoreTopology

Summary: Set the mesh topology reference on the core. Called by EditorCollab after creating the topology manager.


isPeerTrusted

Summary: Check if a peer is trusted (high reputation) and should get a direct connection even in relay mode. Trusted peers bypass the relay host for lower latency.


getPeerConnectionTier

getPeerConnectionTier(core, peerId, peerScore, fastChannel)

Summary: Get the connection tier label for a peer.


getMaxBufferedAmount

Summary: Get the maximum bufferedAmount across all peer ops channels. Used by CollabMessageRouter for backpressure detection.


getSelfId

Summary: Get the local peer ID.


getPeers

Summary: Get all connected peers.


connectCollabPeer

connectCollabPeer(core, peerId, username = 'Remote')

Summary: Begin a targeted WebRTC connection to an already-discovered peer.


disconnectCollabPeer

Summary: Close one direct peer connection without tearing down discovery.


observeCollabPeerMetrics

Summary: Collect locally observed transport quality. Remote peers cannot supply or inflate these values, which makes them suitable input for topology roles.


destroyCollabCore

Summary: Destroy all connections and signaling.


deriveCollabChannelId

Summary: Derive a shared AES-256-GCM key from ECDH key exchange. Called after hello handshake when both peers have ECDH public keys.


broadcastGoodbye

Summary: Broadcast a graceful goodbye to all peers before leaving. This lets other peers immediately remove us instead of waiting for heartbeat timeout or WebRTC state change.


collab/CollabCrypto

Summary: CollabCrypto.js ECDH P-256 key exchange + AES-256-GCM encryption for P2P collab channels. Uses the SubtleCrypto API with shared finite-number admission. Flow: 1. Each peer calls generateKeyPair() on connect 2. Peers exchange public keys during hello handshake 3. deriveSharedKey() produces an AES-256-GCM key via ECDH + HKDF 4. All subsequent messages: encrypt() before send, decrypt() on receive 5. HMAC for challenge-response authentication during handshake Nonce scheme: 12 bytes = [8-byte channelId | 4-byte monotonic counter] Counter MUST never repeat for a given key.


generateKeyPair

Summary: Generate an ECDH P-256 key pair for key exchange.


importPublicKey

Summary: Import a raw public key (65 bytes) received from a remote peer.


deriveSharedKey

async deriveSharedKey(privateKey, remotePublicKey, salt)

Summary: Derive a shared AES-256-GCM key from our private key + remote public key. Optionally mixes in a room password for authentication.


deriveHmacKey

async deriveHmacKey(privateKey, remotePublicKey, salt)

Summary: Derive an HMAC-SHA256 key from ECDH shared bits for challenge-response auth.


cryptoNonceReport

cryptoNonceReport(counter, channelId = null)


encryptedMessageReport

encryptedMessageReport(message, channelId = null)


encrypt

async encrypt(key, plaintext, counter, channelId)

Summary: Encrypt a msgpack payload with AES-256-GCM.


decrypt

async decrypt(key, message, channelId, replayState = null)

Summary: Decrypt an AES-256-GCM encrypted message.


generateChallenge

Summary: Generate a random 32-byte challenge.


signChallenge

Summary: Sign a challenge with HMAC-SHA256.


verifyChallenge

async verifyChallenge(hmacKey, challenge, signature)

Summary: Verify a challenge signature.


createPeerCryptoState

Summary: Create crypto state for a single peer connection.


initPeerCrypto

Summary: Initialize crypto state with a fresh key pair.


completePeerCrypto

async completePeerCrypto(state, remotePublicKeyRaw, roomPassword)

Summary: Complete the handshake: import remote public key, derive shared keys.


isCryptoAvailable

Summary: Check if crypto is available in this environment.


collab/CollabDivergenceTracker

Summary: CollabDivergenceTracker.js Records per-frame position hashes during play mode and compares them across peers to detect simulation divergence. Hash algorithm: FNV-1a 32-bit over quantized entity positions.


recordLocalFrame

Summary: Record a local frame hash. Call every simHistoryInterval during play.


receiveRemoteHash

receiveRemoteHash(tracker, peerId, simTime, frameHash)

Summary: Receive a remote heartbeat hash from a peer.


removePeerDivergence

Summary: Remove a peer's data on disconnect.


getDivergenceLog

Summary: Get all recorded divergence events.


resetDivergenceTracker

Summary: Clear all tracking data (call on exitPlayMode).


exportDivergenceReport

Summary: Export a JSON report of all divergences + local timeline.


computeFrameHash

Summary: Compute a fast FNV-1a hash of all entity positions in the scene. Quantizes positions to 3 decimal places for float stability.


collab/CollabEntityAuthority

Summary: CollabEntityAuthority.js Per-entity ownership model for collaborative multiplayer. Every entity has at most one "owner" peer. The owner is the sole source of truth for that entity's transform, physics, and interactive state. Ownership transfers via explicit claim/release ops — no server needed. Priority levels (higher number wins): 0 DEFAULT   — physics authority (host) owns unclaimed entities 1 PROXIMITY — auto-assigned to nearest peer's camera (future) 2 SELECTED  — peer clicked/selected the entity in the inspector 3 GRABBED   — peer is actively dragging/manipulating the entity Conflict resolution: - Higher priority always wins (grab beats select beats proximity) - Equal priority: earlier timestamp wins (first-come-first-served) - Owner can always release voluntarily - Stale claims (no refresh for STALE_TIMEOUT_MS) are auto-released Inspired by: Photon Takeover policy, Gaffer on Games authority scheme, Roblox Network Ownership, Source Engine shadow controllers.


createEntityAuthority

Summary: Create an entity authority state object.


getEntityOwner

Summary: Get the current owner of an entity.


isLocallyOwned

Summary: Check if the local peer owns an entity.


isEntityClaimed

Summary: Check if an entity is owned by anyone (not default authority).


getEntitiesOwnedBy

Summary: Get all entities owned by a specific peer.


getLocallyOwnedSet

Summary: Get all locally owned entity IDs as a Set (for fast lookup in sync loops).


shouldBroadcastEntity

Summary: Check if the local peer should broadcast transforms for an entity. Returns true if: (a) we own it, or (b) nobody owns it and we're play authority.


shouldAcceptRemoteTransform

Summary: Check if the local peer should accept remote transforms for an entity. Returns true if we do NOT own it.


claimEntity

Summary: Claim ownership of an entity at a given priority. Returns the op to broadcast, or null if claim was rejected locally.


releaseEntity

Summary: Release ownership of an entity. Reverts to default authority. Returns the op to broadcast, or null if we didn't own it.


releaseAllLocal

Summary: Release ALL claims owned by the local peer (e.g. on disconnect or exit play mode). Returns array of ops to broadcast.


onRemoteAuthorityOp

Summary: Handle an incoming entity_authority op from a remote peer. Applies the claim/release to local state after conflict resolution. Returns true if the claim was accepted.


tickAuthority

Summary: Build refresh ops for all local claims (call at ~2Hz from the collab loop). Also prunes stale remote claims.


onPeerDisconnected

Summary: Handle a peer disconnecting — release all their claims.


getHandoffBlend

Summary: Get handoff blend factor for an entity (0 = fully old owner, 1 = fully new owner). Used by interpolation to smoothly blend during ownership transfer.


onOwnershipChanged

Summary: Set callback for ownership changes.


destroyEntityAuthority

Summary: Destroy / reset authority state.


collab/CollabFastChannel

Summary: CollabFastChannel.js Low-overhead data transport for same-origin peers via BroadcastChannel. Problem: WebRTC DataChannels add ~50-100ms RTT even on localhost due to DTLS encryption, SCTP transport, and msgpack encode/decode overhead. Solution: For peers sharing the same browser origin (same-machine testing, same-domain deployment), use the BroadcastChannel API which provides: - Structured clone transfer (no msgpack encode/decode) - No encryption overhead (same-origin security model) - Browser-scheduled local delivery without ICE/STUN/DTLS setup - Zero setup — no ICE/STUN/DTLS negotiation This module creates a dedicated data BroadcastChannel (separate from the signaling channel in CollabSignal.js) that carries ops and presence. CollabCore detects same-origin peers and prefers this transport. For cross-origin / cross-machine peers, WebRTC is still used. Inspired by: Meshtastic's tiered transport (LoRa → BLE → WiFi), choosing the fastest available link per peer.


createFastChannel

createFastChannel(projectId, selfId, callbacks = {})

Summary: Create a fast channel instance.


fastChannelSendOp

Summary: Send an op via fast channel to all same-origin peers. Uses structured clone (no msgpack encode/decode).


fastChannelSendPresence

Summary: Send presence data via fast channel.


fastChannelSendToPeer

Summary: Send an op to a specific peer via fast channel.


isFastPeer

Summary: Check if a specific peer is reachable via fast channel.


getFastPeerIds

Summary: Get the set of all fast peer IDs.


getFastChannelStats

Summary: Get fast channel stats.


destroyFastChannel

Summary: Destroy the fast channel. Sends a bye message to peers first.


collab/CollabHostMigration

Summary: CollabHostMigration.js Automatic host migration for P2P collaboration sessions. Problem: When the host peer drops (closes tab, network outage, crash), relay mode breaks — all non-host peers lose the ability to communicate through the relay hub. The session effectively dies. Solution: Bully-style host election with reputation-weighted scoring. When the host is detected as dead: 1. All peers start a migration election 2. The peer with the highest "candidacy score" wins 3. New host assumes relay duties and notifies all peers 4. If the original host returns, it can reclaim the role Election Algorithm (modified Bully): - Each peer computes a candidacy score based on: • Reputation score (40%) — trustworthy peers preferred • Uptime in session (30%) — stable peers preferred • Connection count (20%) — well-connected peers preferred • Deterministic tiebreaker (10%) — lexicographic selfId for consistency - All peers broadcast their candidacy - After a short election timeout, the highest-scoring peer wins - If two peers tie, the one with the lexicographically higher selfId wins Inspired by: - Xbox Live host migration (Halo 2/3) - Photon PUN MasterClient migration - Raft leader election (simplified — no log replication needed) - Bully algorithm (Garcia-Molina, 1982) Current scope: this module elects a relay host and reconfigures routing. Authority-map handoff, replicated-log consensus, election terms/quorums, and signature-gated dispatch are not implemented here and remain audit gaps. Candidate reputation/connection metrics remain locally self-reported, and captured multi-peer host-failure/rejoin traces remain absent.


createHostMigration

createHostMigration(selfId, config = {})

Summary: Create a host migration manager.


startHostMigration

Summary: Start host migration monitoring.


migrationSetHost

Summary: Set the host peer ID (for joiners who didn't know at startup). Starts the host liveness monitor if not already running.


stopHostMigration

Summary: Stop host migration monitoring.


hostHeartbeatReceived

Summary: Record that we received a heartbeat (or any message) from the host. Call this from the heartbeat handler.


hostActivityReceived

Summary: Record that we received ANY op from a peer (used for liveness). Even non-heartbeat ops prove the host is alive.


onElectionCandidacy

Summary: Handle a received election candidacy from another peer.


onElectionResult

Summary: Handle a received election result from another peer.


onHostReclaimRequest

Summary: Handle the original host returning to the session. The original host broadcasts a reclaim request. The current acting host defers after a grace period, handing duties back.


requestHostReclaim

Summary: Called by the original host when it returns to reclaim. Only call if we ARE the original host and someone else took over.


hostMigrationCandidacyScoreReport

hostMigrationCandidacyScoreReport(metrics = {}, timestampMs = Date.now()


migrationAddPeer

Summary: Register a known peer.


migrationRemovePeer

Summary: Remove a known peer.


migrationUpdateSelfMetrics

Summary: Update our own metrics (call from collab loop).


onHostElectionOp

Summary: Handle an incoming host_election op. Routes to the appropriate handler based on phase.


buildGoodbyeOp

Summary: Build a goodbye op to broadcast before leaving. Other peers use this to immediately detect departure instead of waiting for heartbeat timeout.


onPeerGoodbye

onPeerGoodbye(hm, payload, fromPeerId = payload?.peerId)

Summary: Handle a received goodbye from a departing peer. If the host sent the goodbye, immediately start election (no timeout wait).


collab/CollabIdentity

Summary: CollabIdentity.js Cryptographic peer identity using ECDSA P-256 (Web Crypto API). Each peer generates an ECDSA key pair on init. The public key's SHA-256 fingerprint becomes the peer's verified identity — unforgeable without the private key. During the hello handshake, peers exchange public keys and a signed proof (signature of their selfId). The remote side verifies the signature, confirming the peer truly owns the claimed public key. Host verification: the host's fingerprint is embedded in the invite code. Joiners verify the host's fingerprint matches the one in the invite code, preventing host impersonation. Reputation receipts: peers sign their score reports. Others verify the signature before trusting the score, preventing score tampering. Flow: 1. createIdentity() → ECDSA P-256 key pair + fingerprint 2. buildIdentityProof(identity, selfId) → signed selfId (for hello) 3. Remote receives proof → verifyIdentityProof(publicKey, selfId, proof) 4. If host: fingerprint embedded in invite code → joiner checks match 5. signScoreReceipt(identity, scoreData) → signed score for reputation 6. verifyScoreReceipt(publicKey, scoreData, signature) → boolean


collabIdentityTimestampReport

collabIdentityTimestampReport(timestamp, nowMs = Date.now()


prepareCollabIdentityRecord

Summary: Validate one bounded v1/v2 identity record without guessing future shapes.


createIdentity

async createIdentity(keyId = COLLAB_IDENTITY_KEY_ID)

Summary: Generate or load an ECDSA P-256 key pair. Tries IndexedDB first for persistent identity across sessions. If not found, generates a new key pair and stores it. The fingerprint (SHA-256 of public key) stays the same across sessions, enabling long-term reputation tracking.


computeFingerprint

Summary: Compute fingerprint from a raw public key (public API for verification).


buildIdentityProof

Summary: Build an identity proof: sign our selfId with our private key. The remote peer verifies this signature to confirm we own the public key.


verifyIdentityProof

async verifyIdentityProof(publicKeyRaw, selfId, signature)

Summary: Verify an identity proof from a remote peer.


collabIdentityScoreReceiptReport

collabIdentityScoreReceiptReport(scoreData, nowMs = Date.now()


signScoreReceipt

Summary: Sign a reputation score receipt. The receipt proves that this peer calculated and reported a specific score for a target peer at a specific time. Other peers verify the signature to prevent score tampering.


verifyScoreReceipt

async verifyScoreReceipt(publicKeyRaw, scoreData, signature, nowMs = Date.now()

Summary: Verify a reputation score receipt from a remote peer.


signData

Summary: Sign arbitrary string data with our identity.


signRawBytes

Summary: Sign raw bytes with our identity (no TextEncoder step) — needed when the verifier expects a signature over an exact byte string it issued (e.g. a random challenge), not over the UTF-8 encoding of some stringified form of it. See signData() for the string-based counterpart.


verifyRawBytes

async verifyRawBytes(publicKeyRaw, bytes, signature)

Summary: Verify a signature on raw bytes. Counterpart to signRawBytes().


verifyData

async verifyData(publicKeyRaw, data, signature)

Summary: Verify a signature on arbitrary string data.


createIdentityRegistry

Summary: Create a registry that tracks verified identities for all connected peers. Each peer's identity is verified once during the hello handshake and then cached. The registry also tracks cross-verified reputation scores.


registerPeerIdentity

async registerPeerIdentity(registry, peerId, publicKeyRaw, identityProof, isHost = false)

Summary: Register a peer's identity after verifying their proof. Called during the enhanced hello handshake.


removePeerIdentity

Summary: Remove a peer's identity from the registry.


getPeerIdentity

Summary: Get a peer's verified identity info.


setExpectedHostFingerprint

Summary: Set the expected host fingerprint (from the invite code).


resetIdentityRegistry

resetIdentityRegistry(registry, options = {})


recordScoreReceipt

async recordScoreReceipt(registry, reporterPeerId, targetPeerId, score, timestamp, signature)

Summary: Record a signed score receipt from a remote peer. Each peer tracks what scores others have reported about them AND about other peers.


getConsensusScore

Summary: Get the consensus score for a peer based on all verified receipts. Returns the median of all verified scores reported by different peers. Also detects disputes (significant score disagreements).


buildScoreReportOp

async buildScoreReportOp(identity, selfId, targetPeerId, score)

Summary: Build a signed score report op for broadcasting to all peers.


getIdentityStats

Summary: Get identity system stats for UI display.


isIdentityAvailable

Summary: Check if crypto identity is available.


isCriticalOp

Summary: Check if an op type requires a signature.


collabIdentityCriticalOpReport

collabIdentityCriticalOpReport(op, senderPeerId, nowMs = Date.now()


signCriticalOp

async signCriticalOp(identity, op, selfId)

Summary: Sign a critical op with our ECDSA identity. Appends `_sig` (signature) and `_pub` (public key) fields to the op. The canonical signed data is: `op-sig:type:senderId:timestamp:payloadHash`


verifyCriticalOp

async verifyCriticalOp(op, senderPeerId, registry)

Summary: Verify a signed critical op from a remote peer.


collabIdentityPayloadHash

Summary: Helper: SHA-256 hash a string to hex (for canonical op signing).


computeTrustLevel

Summary: Compute the trust level for a peer.


getTrustColor

Summary: Get the display color for a trust level.


getTrustLabel

Summary: Get the display label for a trust level.


collab/CollabIntegrityVerifier

Summary: CollabIntegrityVerifier.js Integrity validation and anomaly tracking for P2P collab. Inspired by: - Hash-chain receipts: valid ops contribute to a local rolling chain. - Server-side validation rules adapted to pairwise peer validation. - Lockstep P2P: periodic state hashing — all peers hash their zone state and exchange hashes for pairwise divergence detection. - FairPlay: operation whitelist — only registered op types are accepted. Unknown op types are rejected and penalized. Components: 1. Op Receipt Chain — local rolling hash chain per peer 2. Zone State Digest — periodic position hash for pairwise comparison 3. Anomaly Detector — physics/spatial rule violations 4. Operation Whitelist — only valid op types accepted 5. Violation Tracker — accumulates strikes, triggers auto-kick 6. Reputation Integration — violations lower peer score Hashes preserve the existing non-cryptographic legacy FNV-like wire format.


collabIntegrityLegacyHash

Summary: Preserve the existing integrity-chain FNV-like string hash. This intentionally keeps the old JavaScript-number multiply path instead of switching to canonical Math.imul FNV-1a, which would change peer digests.


createIntegrityVerifier

createIntegrityVerifier(selfId, config = {})

Summary: Create an integrity verifier.


validateIncomingOp

Summary: Validate an incoming op from a peer. Checks: 1. Op type is in the whitelist 2. Op has valid structure 3. Entity position changes are within physics limits


recordOpInChain

Summary: Record an op in the peer's hash chain for tamper-evidence. Call after validateIncomingOp succeeds.


getChainHead

Summary: Get the current chain head hash for a peer (for verification exchange).


computeZoneDigest

computeZoneDigest(verifier, sceneEntities, ecsWorld, getEntityComponent, authoritySet)

Summary: Compute a hash digest of all entity positions in the caller's zone. This is exchanged with peers for consensus-based state verification.


buildDigestOp

Summary: Build an integrity_digest op to broadcast.


onRemoteDigest

Summary: Handle an incoming integrity_digest from a remote peer. Compares against our own digest — mismatch indicates desync or tampering.


tickIntegrity

Summary: Tick the verifier: decay violations, build trust, compute social influence. Call at ~1Hz.


getPeerViolationCount

Summary: Get the violation count for a peer.


getPeerViolations

Summary: Get recent violations for a peer.


getAllViolationSummaries

Summary: Get all peer violation summaries.


getPeerTier

Summary: Get the current tier for a peer.


getPeerTrust

Summary: Get trust level for a peer (0.0-1.0).


setPlayMode

Summary: Set play mode flag — violations during play mode are weighted less.


isPeerThrottled

Summary: Check if a peer's ops should be throttled (tier >= THROTTLED).


isPeerMuted

Summary: Check if a peer's scene-edit ops should be blocked (tier >= MUTED).


getIntegrityStats

Summary: Get verifier stats.


buildCapabilitiesOp

Summary: Build a __capabilities__ op to send to a peer on connect. Contains our supported op types and version string so the remote peer can be lenient about ops we don't recognize (version mismatch, not cheating).


registerPeerCapabilities

Summary: Register a remote peer's declared capabilities. Called when we receive a __capabilities__ op from them. Unknown ops from peers with declared capabilities that include the op are treated as version mismatches (INFO), not cheating (HIGH).


setLocalVersion

Summary: Set the local version string for capability comparison.


integrityRemovePeer

Summary: Remove a peer's tracking state.


destroyIntegrityVerifier

Summary: Destroy verifier state.


collab/CollabMeshTopology

Summary: CollabMeshTopology.js Adaptive mesh topology for scalable P2P collaboration. Problem: Full mesh is O(N²) connections. At 8+ WebRTC peers, browsers struggle with connection management, DTLS overhead, and bandwidth. The old system had a hard limit of 4-8 peers before switching to star relay (single point of failure, bottleneck on host). Solution: Hybrid topology combining proven P2P techniques: 1. PARTIAL MESH — Each peer maintains K direct WebRTC connections to its "best" neighbors (lowest latency, highest reputation). K = 3-6 instead of N-1. Scales linearly, not quadratically. 2. GOSSIP PROPAGATION — Ops ripple through the partial mesh. Each peer forwards received ops to its neighbors (minus sender). With K=4, messages reach all 50 peers in ~3 hops (log_K(N)). Deduplication via op fingerprint prevents infinite loops. 3. SUPERNODE ELECTION — High-reputation peers (score ≥ 700) with good uptime and low latency are elected as supernodes. They maintain more connections (up to 2K) and serve as relay hubs between clusters. Inspired by Skype's original P2P architecture. 4. FAST CHANNEL BRIDGING — Same-origin peers use BroadcastChannel (zero cost). They don't count toward K and act as free bridges. Topology modes (auto-selected based on peer count): - FULL_MESH:   ≤6 WebRTC peers → everyone connects to everyone - PARTIAL_MESH: 7-50 peers → K-neighbor gossip mesh - SUPERNODE:    20+ peers → supernodes bridge clusters Inspired by: libp2p (IPFS), Kademlia, Bitcoin gossip, Meshtastic, Skype supernode architecture, and epidemic broadcast protocols. Current policy boundaries: topology callbacks recommend connection changes but EditorCollab does not execute them; topology reports are advisory rather than consensus; gossip envelopes are forwarded before application-level integrity validation; and captured large-room WebRTC traces remain absent.


createMeshTopology

Summary: Create a topology manager.


resolveTopology

Summary: Resolve the current topology mode based on peer count and quality. Called after peer join/leave and during periodic rebalance.


topologyAddPeer

topologyAddPeer(topo, peerId, info = {})

Summary: Register a discovered peer (from signaling).


topologyRemovePeer

Summary: Remove a peer (disconnected / left).


topologyPeerConnected

Summary: Mark a peer as connected (WebRTC DataChannel open).


topologyPeerDisconnected

Summary: Mark a peer as disconnected.


topologyUpdatePeerMetrics

Summary: Update a peer's metrics (called from reputation system).


computeIdealNeighbors

Summary: Compute the ideal neighbor set based on current metrics. Returns { connect: string[], disconnect: string[] } — peers to add/remove.


evaluateSupernodeStatus

evaluateSupernodeStatus(topo, selfScore, selfLatencyAvg, integrityInfo)

Summary: Evaluate whether this peer should be a supernode. Criteria: high reputation, low latency, good uptime, clean integrity, trusted. Peers with violations (throttled/muted/kicked) are immediately disqualified. Trust and social standing boost candidacy score.


topologyUpdatePeerIntegrity

Summary: Update a peer's integrity data (called from integrity verifier tick).


evaluateRemoteSupernodes

Summary: Evaluate all remote supernodes and demote any that have degraded. Called during rebalance tick. Demoted supernodes are unregistered and a topology change is triggered so the mesh re-routes around them.


topologyRegisterSupernode

topologyRegisterSupernode(topo, peerId, announcement = null)

Summary: Register a remote peer as a supernode.


topologyUnregisterSupernode

Summary: Unregister a remote peer as a supernode.


topologyApplyInfo

Summary: Validate and apply one advisory topology_info payload atomically. The report may expand discovery knowledge but never mutates live routes.


gossipFingerprint

Summary: Generate a gossip fingerprint for deduplication. Uses a compact hash of the op to avoid forwarding the same op twice.


gossipWrap

Summary: Wrap an op for gossip propagation. Adds gossip metadata: origin, TTL, hop count, fingerprint.


gossipReceive

Summary: Process an incoming gossip-wrapped op. Returns { forward: boolean, op: Object, targets: string[] } - forward: true if this op should be forwarded to neighbors - op: the op with decremented TTL - targets: peerIds to forward to (excludes sender and origin)


startTopology

Summary: Start the periodic rebalance timer.


stopTopology

Summary: Stop the rebalance timer.


getTopologyMode

Summary: Get current topology mode.


shouldGossip

Summary: Check if we should use gossip forwarding (partial/supernode mode).


isSupernode

Summary: Check if we're a supernode.


getNeighborIds

Summary: Get the list of our current neighbor peerIds.


getTopologyStats

Summary: Get topology stats for UI display.


buildSupernodeAnnounce

Summary: Build a supernode announcement op to broadcast. Peers use this to discover supernodes.


buildTopologyInfoOp

Summary: Build a topology_info op for neighbor exchange. Peers periodically share their neighbor lists so the mesh can discover better paths (inspired by BGP route sharing).


destroyMeshTopology

Summary: Destroy the topology manager.


rebalanceTopologyNow

Summary: Run one topology rebalance immediately (useful after discovery/metrics changes).


collab/CollabMessageRouter

Summary: CollabMessageRouter.js Priority-based message orchestration for collab sync. Sits between EditorCollab and CollabCore, providing: - 4 priority lanes (critical → ephemeral) - Bandwidth monitoring via channel.bufferedAmount - Transform coalescing (latest position wins per entity) - Adaptive rate control under backpressure Priority lanes: 0 — Critical:  play_sync, __kick__, snapshot         → immediate, never throttled 1 — Realtime:  play transforms/PBD/grab force        → coalesced or rate-budgeted 2 — Scene:     create, delete, transform, property…  → ordered, rate-budgeted 3 — Ephemeral: sim_heartbeat                          → latest-only, droppable


createMessageRouter

Summary: Create a message router instance.


routerEnqueue

Summary: Enqueue an op for prioritized delivery.


routerFlush

Summary: Flush all queued ops respecting priority and backpressure. Call once per render frame from the render loop.


routerStart

Summary: Start automatic flush timer (call once on init).


routerStop

Summary: Stop the automatic flush timer.


getRouterStats

Summary: Get router statistics.


resetRouter

Summary: Reset router state (on disconnect/reconnect).


destroyRouter

Summary: Destroy router.


collab/CollabOpQueue

Summary: CollabOpQueue.js Op ordering, deduplication, and per-peer sequence gap handling. Each op: { id, peerId, seq, type, payload, ts } Current scope: bounded local ordering for admitted logical peer IDs. Sequence epochs/wrap, retransmission/NACK, cryptographic sender binding, and captured loss/reordering transport traces remain audit gaps.


enqueueOp

Summary: Enqueue an incoming op. Returns array of ops ready to apply.


flushPeer

Summary: Flush all held ops for a peer (called on timeout or peer leave).


removePeer

Summary: Remove all state for a peer (on disconnect).


tickOpQueue

Summary: Tick — flush any peers with stale gaps.


collab/CollabPeerReputation

Summary: CollabPeerReputation.js Peer quality scoring system — rates each peer 0-1000 based on real-time connection metrics. Inspired by Meshtastic SNR-based prioritization and multi-layer throttling. Score breakdown (1000 total): 250 — Latency (RTT from heartbeat pings) 200 — Packet delivery (sent vs received ratio) 200 — Connection stability (reconnection count) 150 — Session duration (longer = more trusted) 100 — Jitter (variance in latency — lower = better) 100 — Throughput quality (buffered amount / backpressure) Each sub-score is 0.0–1.0, multiplied by its weight. The total is rounded to an integer 0–1000. Scores update every 2 seconds; observed peer scores are shared through signed reports, while the local self-score rides pongs. Inspired by: Meshtastic SNR-based node prioritization, role-based priority, and automatic traffic scaling for large meshes.


createPeerReputation

Summary: Create a reputation tracker for all peers.


reputationAddPeer

Summary: Register a new peer.


reputationRemovePeer

Summary: Remove a peer.


buildPingOps

Summary: Build ping ops to send to all peers. Call at PING_INTERVAL_MS.


buildPongOp

Summary: Build a pong response to an incoming ping.


onPongReceived

Summary: Handle an incoming pong from a peer. Records RTT and jitter.


recordBufferedAmount

Summary: Record the current bufferedAmount for a peer's data channel. Call this periodically (e.g., every presence tick).


tickReputation

Summary: Recompute scores for all peers. Call at ~0.5Hz.


getPeerScore

Summary: Get the reputation score for a specific peer (0-1000).


getPeerMetrics

Summary: Get full metrics for a specific peer (for debug / stats display).


getSelfScore

Summary: Get the local peer's self-score. This remains the bootstrap score until a self-observation consensus path exists.


getAllPeerScores

Summary: Get all peer scores as an array of { peerId, score, subScores, avgRtt, avgJitter }.


getScoreColor

Summary: Get the score color for rendering (green → yellow → red gradient).


getScoreLabel

Summary: Get a human-readable label for a score range.


destroyPeerReputation

Summary: Destroy reputation state.


collab/CollabPlaySync

Summary: CollabPlaySync.js Synchronizes play/pause/stop across all connected peers. Broadcasts a shared determinism seed so all simulations run identically. Sends heartbeat hashes every 1s for divergence detection.


broadcastPlay

Summary: Broadcast a play command to all peers. Called when local user hits Play. seed: optional 32-bit int. Generated here if not provided.


broadcastPause

Summary: Broadcast a pause command to all peers.


broadcastStop

Summary: Broadcast a stop command to all peers.


onRemotePlayCommand

Summary: Handle an incoming play_sync op from a remote peer. Returns the action taken: 'play' | 'pause' | 'stop' | null


onRemoteHeartbeat

Summary: Handle an incoming sim_heartbeat op from a remote peer.


exportTimeline

Summary: Export local simHistory as a downloadable JSON file.


isRemotePlay

Summary: Check if the current play command was triggered remotely.


getCurrentSeed

Summary: Get the current determinism seed.


collab/CollabPresence

Summary: CollabPresence.js Tracks all peers' camera positions, usernames, selections, and colors. Presence packet: { v:1, u:username, c:[x,y,z], t:[tx,ty,tz], s:[entityIds] } Current scope: lossy latest-state presence. Authenticated presence identity, sequence/freshness fields, cross-system stale-peer cleanup, interpolation ownership, and captured churn traces remain audit gaps.


buildLocalPresencePacket

buildLocalPresencePacket(username, cameraPos, cameraTarget, selectionIds, simTime, mode, userColor)

Summary: Build a presence packet from local state to broadcast.


applyRemotePresence

Summary: Apply a remote presence packet for a peer.


addPeer

Summary: Register a peer joining (before first presence packet).


removePresence

Summary: Remove a peer (on disconnect).


getPresenceList

Summary: Get list of all known peers with their presence data.


getPeerColor

Summary: Get color for a specific peer.


pruneStalePresence

Summary: Prune peers not seen for > 30s.


collab/CollabSceneSync

Summary: Convert an EditorHistory action into a collab op payload.


collabSceneValueReport

collabSceneValueReport(value, options = {})


opToAction

Summary: Convert a collab op back into an EditorHistory-compatible action.


serializeSnapshot

Summary: Serialize the full scene state into a compact snapshot for new joiners.


applySnapshot

Summary: Apply a full scene snapshot to the editor (for new joiners). Clears existing scene and rebuilds from snapshot.


Summary: CollabSceneSync.js Serializes EditorHistory actions <-> collab ops. Handles full scene snapshots for new joiners.


collab/CollabSignal

Summary: CollabSignal.js 4-tier signaling abstraction for WebRTC peer discovery. Tier 1: BroadcastChannel     — same machine, instant, offline Tier 2: SharedWorker         — same machine multi-tab relay Tier 4: Particle Masterserver — PRIMARY cross-internet signaling relay. Pinned V2 endpoints use the shared ParticleNetworkDaemon; legacy V1 is accepted only when that endpoint explicitly opts in. Uses the same room/route-scoped HELLO/OFFER/ANSWER/CANDIDATE dispatch as Tiers 1/2 (see _handleSignalMessage) rather than a binary swarm protocol — the Masterserver just fans opaque JSON out to every other session attached to the same locally-derived opaque route. This is server-mediated, so it works across NATs/networks without any third-party dependency. Server-configured via localStorage 'os.network.masterServers' (same list the Particle Network control panel app manages); falls back to the pinned production bootstrap if unset. Tier 3: BitTorrent WSS       — EXPLICIT FALLBACK ONLY. It remains off by default and can run only after the user opts in. A V2 integrity failure always fails closed and suppresses legacy/BitTorrent downgrade. Tiers 1/2/4 start immediately; Tier 3 is deferred/conditional (see above). First tier to form a WebRTC connection to a given peer wins — after that, signaling is no longer needed for that peer.


createSignalChannel

Summary: Create a signal channel. config: { projectId, selfId, username, onOffer, onAnswer, onCandidate, onPeerJoin, onPeerLeave } Negotiation callbacks receive (peerId, payload, offerId). Generic signaling is deliberately strict: every OFFER/ANSWER/CANDIDATE is addressed to one peer and correlated to one offer generation. The WebTorrent tier retains its independent tracker offer_id lifecycle and does not use these envelopes.


fetchTurnCredentials

Summary: Fetch TURN relay credentials from Cloudflare Pages Function. ONLY called as a fallback when STUN-only P2P connection fails. TURN relays ALL traffic through Cloudflare = expensive. Avoid unless necessary.


startSignaling

Summary: Start all 3 signaling tiers simultaneously. Uses FREE STUN servers only — no Cloudflare data cost. TURN relay is only fetched on-demand when a direct P2P connection fails.


forceAnnounce

Summary: Force an immediate discovery re-announce across all active tiers, bypassing the periodic announce timers. Used by app-layer "Connect now" so a known contact is found without waiting for the next BC hello / tracker announce cycle.


collabSignalTextReport

collabSignalTextReport(value, maxBytes, options = {})


sendOffer

sendOffer(channel, peerId, sdpOffer, offerId)

Summary: Send one SDP offer to one discovered peer on the generic signaling tiers.


broadcastOffer

broadcastOffer(channel, peerId, sdpOffer, offerId)

Summary: Retained as a strict compatibility symbol. Untargeted offers are unsafe in a multi-peer room, so callers must now supply the target and offer generation.


sendAnswer

sendAnswer(channel, peerId, sdpAnswer, offerId)

Summary: Send an SDP answer to a specific peer.


sendCandidate

sendCandidate(channel, peerId, candidate, offerId)

Summary: Send an ICE candidate to a specific peer.


destroySignalChannel

Summary: Destroy all signaling resources.


collabSignalOfferPlanReport

collabSignalOfferPlanReport(pendingCount, inFlightCount = 0)


collabSignalHelloReport

collabSignalHelloReport(message, selfId = '')


collabSignalPeerHelloReport

collabSignalPeerHelloReport(message, selfId = '')


collab/CollabSpatialAuthority

Summary: CollabSpatialAuthority.js Distributed spatial authority for collaborative multiplayer. Each peer owns a circular "zone" centered on their camera position. Entities within a peer's zone are simulated locally by that peer. Entities outside all zones are dormant (no physics, no sync). Authority resolution (Roblox + Gaffer hybrid): - Nearest peer within zone radius wins (automatic proximity) - 20% hysteresis prevents ping-pong at boundaries - Higher-priority claims (SELECTED, GRABBED) override proximity - Sequence numbers resolve conflicts (Gaffer-style) Interest management (Photon-style): - Interest area = zoneRadius × interestMultiplier (default 1.3×) - Only send state for entities you're authoritative over - Only receive state for entities in your interest area Inspired by: Star Citizen dynamic server meshing, Roblox Network Ownership, Photon Fusion Shared Authority + AoI grid, Gaffer on Games authority scheme.


createSpatialAuthority

Summary: Create a spatial authority state.


computeZoneRadius

Summary: Auto-compute zone radius from scene bounds.


setZoneRadius

Summary: Update the zone radius (e.g., after scene bounds change or manual override). Rebuilds the spatial grid with the new cell size.


updatePeerPosition

Summary: Update a peer's camera position (called from presence packets).


removePeer

Summary: Remove a peer (on disconnect).


updateEntityPosition

updateEntityPosition(auth, entityId, x, z)

Summary: Update an entity's position in the spatial grid. Call this when entity transforms change (from physics step or remote sync).


removeEntity

Summary: Remove an entity from the spatial grid (on entity delete).


resolveAuthority

Summary: Resolve authority for a single entity based on proximity. Applies hysteresis: current owner keeps it unless new peer is 20% closer.


getMyAuthoritySet

Summary: Get all entity IDs that the local peer is authoritative over. Uses the auto-authority map (resolved at RESOLVE_INTERVAL_MS).


getMyInterestSet

Summary: Get all entity IDs within the local peer's interest area (zone × interestMultiplier).


getPeerInterestSet

Summary: Get the interest set for a specific peer (used by relay host for filtering).


isInMyZone

Summary: Check if an entity position is within the local peer's authority zone.


isInBufferZone

Summary: Check if an entity position is within the buffer zone (boundary ring).


isDormant

Summary: Check if an entity is dormant (outside all peer zones).


tickSpatialAuthority

Summary: Periodic tick: re-resolve auto-authority for all tracked entities. Call this at ~2Hz from the collab loop (or faster if needed).


destroySpatialAuthority

Summary: Clear all state (on leave room / exit play mode).


collab/CollabTransformSync

Summary: CollabTransformSync.js Broadcasts entity transform updates during play mode so grabbed/moved objects are visible on all peer screens in real time. Authority model: - The peer who presses Play becomes the physics authority. - Authority broadcasts ALL entity transforms at ~10Hz. - Non-authority peers receive and apply transforms, skipping local physics. - Grabbed entity transforms are always sent at ~15Hz by anyone.


createTransformSync

Summary: Create a transform sync state object.


trackLocalGrab

Summary: Mark an entity as being actively grabbed/dragged by the local user.


untrackLocalGrab

Summary: Unmark an entity when released.


buildPlayTransformOp

Summary: Build a play_transforms op for grabbed entities only. Returns null if nothing to broadcast or if throttled.


buildFullPlayTransformOp

buildFullPlayTransformOp(syncState, ecsWorld, getEntityComponent, sceneEntities, entityAuthority, spatialAuthoritySet)

Summary: Build a play_transforms op for ALL scene entities. Only called by the physics authority at ~10Hz.


buildPbdStateOp

Summary: Build a play_pbd_state op that snapshots PBD particle positions. Any peer running physics simulations broadcasts at ~30Hz for real-time rope sync. Only includes sims with >0 particles. Full float precision for smooth visual sync.


applyRemotePbdState

Summary: Apply incoming PBD particle state from the authority (host). Non-authority clients do NOT run their own PBD solver for these sims — they just apply the received positions directly. Tags each sim with `_remoteAuthority = true` so stepPhysicsSimulations skips the local solver for it (prevents local physics from fighting the authoritative positions).


applyRemotePlayTransforms

applyRemotePlayTransforms(op, peerId, ecsWorld, getEntityComponent, setEntityComponent, syncState)

Summary: Apply incoming play_transforms from a remote peer. Pushes transforms into the interpolation buffer, measures jitter for adaptive delay, and detects "nuke" events (massive state changes) for instant snap.


interpolateRemoteEntities

interpolateRemoteEntities(syncState, ecsWorld, getEntityComponent, localDragEntityId, entityAuthority)

Summary: Interpolate buffered remote transforms and write to ECS + physics. Called every render frame by non-authority peers. Uses adaptive render delay that auto-adjusts based on measured packet jitter. On LAN/good connections: as low as ~20ms. On jittery links: up to 150ms. Slowly tightens delay when stable; instantly widens on late arrivals.


getInterpDelay

Summary: Get the current adaptive interpolation delay (ms) for diagnostics.


getRemoteGrabGlows

Summary: Get set of entity IDs currently being grabbed by remote peers. Returns Map<entityId, peerColor> for glow rendering.


destroyTransformSync

Summary: Destroy / reset transform sync state.


collab/CollabZoneHandoff

Summary: CollabZoneHandoff.js Smooth authority transfer protocol when entities cross spatial zone boundaries. Buffer zone (15% of zone radius): - Entity enters buffer → both peers simulate, current owner broadcasts state - Entity crosses buffer center → handoff op with full state snapshot - 500ms hysteresis prevents rapid back-and-forth Handoff op: { type: 'zone_handoff', payload: { entityId, fromPeer, toPeer, seq, state } } - state: { pos, rot, vel, angVel, pbdParticles? } - seq: authority sequence number (higher always wins, Gaffer-style) Assembly awareness: if entity A is constrained to entity B, both get handed off together to the same peer. Fallback: if fromPeer disconnects mid-handoff, nearest remaining peer takes ownership immediately. Inspired by: Roblox "buffer zone" (red outline), Star Citizen entity graph authority transfers, Gaffer on Games ownership sequence numbers.


createZoneHandoff

Summary: Create a zone handoff manager.


initiateHandoff

initiateHandoff(handoff, entityId, fromPeer, toPeer)

Summary: Start a pending handoff for an entity (called when spatial authority detects a zone change). Does NOT commit yet — waits for hysteresis period.


cancelHandoff

Summary: Cancel a pending handoff (entity moved back to original zone).


buildHandoffOp

buildHandoffOp(entityId, fromPeer, toPeer, seq, entityState)

Summary: Build a zone_handoff op with full entity state for network transmission. Called by the outgoing peer when handoff commits.


captureEntityState

captureEntityState(ecsWorld, getEntityComponent, entityId, physicsSimulations)

Summary: Capture entity state for the handoff snapshot.


onRemoteHandoffOp

onRemoteHandoffOp(handoff, op, selfId, ecsWorld, setEntityComponent, getEntityComponent, physicsSimulations)

Summary: Handle an incoming zone_handoff op from a remote peer. Applies the state snapshot if the sequence number is higher than our last.


tickZoneHandoff

Summary: Tick pending handoffs. Commits any that have passed the hysteresis period. Prunes stale pending handoffs.


onHandoffPeerDisconnect

Summary: Handle peer disconnect: cancel pending handoffs involving this peer and reassign any entities that were mid-handoff.


onHandoffComplete

Summary: Set callback for handoff completion.


destroyZoneHandoff

Summary: Destroy handoff state.


collab/PhysicsGrab

Summary: PhysicsGrab.js Mass-proportional spring-damper (PD controller) physics grab system. How other games do it: - Garry's Mod Physgun:     pure spring-damper, body stays dynamic - Half-Life 2 gravity gun: spring + angular correction torque - Boneworks/BONELAB VR:    PD controller with grip strength cap - UE4 Physics Handle:      D6 joint linear drive to target pos This implementation uses a **frequency / damping-ratio** parameterization (ref: Orange Duck "Spring-It-On", Gaffer on Games "Spring Physics"): omega = 2π × frequency         (natural frequency, rad/s) kp    = omega² × mass           (spring stiffness, N/m)  ← mass-proportional kd    = 2 × ζ × omega × mass    (damping, N·s/m)       ← mass-proportional With ζ = 1.0 (critical damping), the object reaches the target as fast as possible with zero overshoot regardless of mass. A 0.5 kg ball and a 200 kg crate both converge at the same rate. Multiple players grabbing the same object creates competing spring forces that resolve naturally through physics — tug-of-war emerges with no special-case code needed. Fatigue model (Boneworks-inspired): - Holding heavy objects drains fatigue over time - Effective strength = baseStrength * (0.2 + 0.8 * fatigue) - Minimum 20% strength even when exhausted - Fatigue recovers quickly when not holding Grip break: - Progressive grip weakening beyond GRIP_WEAKEN_DIST - Full slip at GRIP_BREAK_DIST Scope: point-target PD forces for dynamic rigid bodies. Constraint/joint drives, rotational grabs, continuous collision detection, solver substep tuning, and captured multi-peer PhysX traces remain outside this module.


physicsGrabVectorReport

physicsGrabVectorReport(value, kind = 'position')


physicsGrabForceReport

physicsGrabForceReport(objPos, objVel, targetPos, strength, mass)


grabForceOperationReport

grabForceOperationReport(op, transportPeerId = null)


admitRemoteGrabForce

admitRemoteGrabForce(remoteGrabs, op, transportPeerId, receivedAtMs = Date.now()


pruneRemoteGrabForces

pruneRemoteGrabForces(remoteGrabs, nowMs = Date.now()


computeGrabForce

computeGrabForce(objPos, objVel, targetPos, strength, mass)

Summary: Compute mass-proportional spring-damper grab force. Uses frequency/damping-ratio parameterization so the convergence rate is identical regardless of object mass. Critical damping (ζ=1) means the object reaches the cursor as fast as possible with zero overshoot.


tickFatigue

Summary: Update fatigue based on load this frame.


effectiveStrength

Summary: Effective strength considering fatigue. Even fully exhausted players retain 20% strength (adrenaline).


canGrab

Summary: Check if an object is too heavy to pick up at all. Returns false if mass > 4x strength (can't apply meaningful force).


createLocalGrab

Summary: Create a local grab state for the player's own grab.


buildGrabForceOp

buildGrabForceOp(peerId, entityId, targetPos, strength, fatigue)

Summary: Build a play_grab_force op to broadcast our cursor target to peers. Other peers apply the same spring force, creating tug-of-war physics.


applyGrabForcesToActor

applyGrabForcesToActor(actor, PhysX, objPos, objVel, grabs, mass)

Summary: Apply all active grab forces to a PhysX actor. Call this BEFORE stepPhysicsWorld each frame.


collab/SpatialHashGrid

Summary: SpatialHashGrid.js O(1) spatial lookup grid for interest management and zone queries. Uses a 2D grid (XZ plane) where each cell stores a Set of entity IDs. Supports radius and AABB queries without iterating all entities. Inspired by: Photon Fusion AoI grid, SpatialOS worker partitioning.


createSpatialHashGrid

createSpatialHashGrid(cellSize, options = {})

Summary: Create a spatial hash grid.


gridInsert

Summary: Insert an entity into the grid.


gridRemove

Summary: Remove an entity from the grid.


gridClear

Summary: Clear all entities from the grid.


gridQueryRadius

Summary: Query all entity IDs within a radius of a point (XZ plane). Returns entities whose grid position is within the radius.


gridQueryRect

gridQueryRect(grid, minX, minZ, maxX, maxZ)

Summary: Query all entity IDs within an axis-aligned bounding box (XZ plane).


gridGetPosition

Summary: Get the stored position for an entity.


gridSize

Summary: Get the total number of entities in the grid.


gridUpdate

Summary: Update an entity's position. Alias for insert (handles cell migration).


collab/index

Summary: engine/collab/index.js Barrel export for all collab modules.


compat/bootloader

Summary: bootloader.js — the Engine Compatibility Bootloader entry point. Boot flow (Phases 1-2 implemented here; 3-7 plug in via ctx hooks): 1. load source              (source-loader) 2. parse HTML               (html-ingestor) 3. static probe → profile   (probe-scanner + profile-builder) 4. merge with cached profile(profile-cache) 5. [validate]               (ctx.validate — schema-registry, Phase 3) 6. [heal]                   (ctx.heal — healing-pipeline, Phase 4) 7. build scoped realm + heal DOM + scope CSS   (runtime-realm) 8. [interpose]              (ctx.interpose — interposers, Phase 5) 9. execute guest scripts 10. cache profile, install error recovery, return an app handle The bootloader is engine-agnostic: pass a `ctx` with optional { storage, profileCache, gpu, scheduler, validate, heal, interpose, onError }.


CompatApp

Summary: A booted guest app handle.


bootExternalApp

async bootExternalApp(options, ctx = {})

Summary: Convenience one-shot boot without holding a CompatBootloader instance.


compat/css-healer

Summary: css-healer.js — scope a guest app's CSS so it cannot style the OS shell. External WebGPU apps routinely style `html, body, canvas, *, :root` globally. `scopeCss(css, scope)` rewrites every style rule so it only applies inside the guest's realm container (e.g. `[data-engine-app="tempolux"]`): html, body            →  [data-engine-app="id"] canvas                →  [data-engine-app="id"] canvas *                     →  [data-engine-app="id"] * .hud .row             →  [data-engine-app="id"] .hud .row At-rules are handled correctly: @media/@supports/@container/@layer/@scope have their inner rules scoped; @keyframes/@font-face/@page/@property/@import are left intact (their "selectors" are not element selectors).


scopeCss

Summary: Scope a full stylesheet string to `scope` (a selector like `[data-engine-app="id"]`).


scopeSelectorFor

Summary: Convenience: the canonical realm scope selector for an app id.


compat/dom-healer

Summary: dom-healer.js — create DOM elements a guest app requires but that are missing. Many single-file WebGPU apps assume `<canvas id="c">`, an overlay `#ov`, a HUD `#hud`, etc. already exist. The compatibility profile lists those in `profile.dom.requiredElements`; this healer ensures each exists inside the guest's realm container BEFORE the guest's scripts run, so `getElementById` and friends resolve instead of throwing.


healDom

Summary: Ensure every `profile.dom.requiredElements` entry exists under `rootEl`.


compat/error-recovery

Summary: error-recovery.js — keep a crashing guest from taking down the OS. Installs scoped error capture and drives a recovery ladder when a guest throws repeatedly within a short window: 1. tolerate isolated errors (already recorded), 2. reboot the realm (fresh parse + heal + run), 3. reboot in SAFE MODE (network off, shader-heal off, GPU limits clamped), 4. render an in-realm error panel with a manual Restart — OS stays alive. Errors counted toward escalation: runtime-error, promise-error, script, gpu-device-lost. Benign infos (warnings, clamps, recovery notices) are ignored.


installErrorRecovery

installErrorRecovery(app, { win = (typeof window !== 'undefined' ? window : null)


makeSafeProfile

Summary: A safer variant of a profile used for safe-mode reboots.


renderErrorPanel

Summary: Render a friendly, non-fatal error panel inside the guest's realm container.


compat/healers/asset-healer

Summary: healers/asset-healer.js — resolve bundled relative asset references. A multi-file guest references `<img src="logo.png">`, `<source src="clip.webm">`, etc. Those relative URLs won't resolve inside the realm, so this pass rewrites any reference present in the package `files` map to a blob URL. The created URLs are recorded on `model._assetBlobs` so the realm revokes them on destroy.


compat/healers/error-healer

Summary: healers/error-healer.js — enable scoped error capture for the guest. Marks the profile so the bootloader installs error/unhandledrejection capture (Phase 7's recovery layer reads `healing.captureErrors`). Kept as its own pass so the recovery policy can grow independently.


compat/healers/gpu-healer

Summary: healers/gpu-healer.js — normalise GPU-related profile fields before boot. Sets a sensible `gpu.presentation` and ensures the runtime healing flags the interposer relies on (`clampGpuLimits`, `wrapQueueSubmit`) are populated. The actual limit-clamping happens at runtime in `interposers/gpu.js`.


compat/healers/lifecycle-healer

Summary: healers/lifecycle-healer.js — ensure lifecycle healing flags are populated. These flags drive the interposers and the recovery layer (Phase 7): whether to recover on GPU context loss, namespace storage, and route downloads safely.


compat/healers/shader-healer

Summary: healers/shader-healer.js — migrate deprecated WGSL attribute syntax. Older WGSL used `[[stage(...)]]` / `[[group(x), binding(y)]]` / `[[location(n)]]` / `[[builtin(...)]]`, which modern WebGPU rejects. This pass rewrites those tokens to the current `@stage` / `@group @binding` / `@location` / `@builtin` forms inside inline guest scripts. The patterns are WGSL-specific and do not occur in normal JS, so the rewrite is safe. Gated by `profile.healing.healShaders`.