@realitycollective/iwsdk-environment
The Meta IWSDK adapter for the Reality Collective WebXR Environment Extensions. Re-exports the engine-free @realitycollective/webxr-environment core, so this is the only package you install.
npm install @realitycollective/iwsdk-environment
Use
Setup is one call, and it registers the system that ticks the directors:
import {
registerEnvironment,
STOCK_PRESETS,
VOID,
} from "@realitycollective/iwsdk-environment";
const env = registerEnvironment(world, {
presets: STOCK_PRESETS,
initial: VOID,
audio: {
cues: [{ id: "hum", src: "/audio/hum.mp3", bus: "ambience", loop: true }],
},
});
env.environment.transition("dusk", { durationMs: 8000 });
Because the tick is an IWSDK system, the environment stops advancing when the session loses focus, exactly like the rest of the app. IWSDK hands systems a delta in seconds and the directors take milliseconds; that conversion happens once, inside the system.
What the adapter does
It drives IWSDK's own machinery rather than reaching past it to three.js:
- Sky - a
DomeGradienton the level root. IWSDK'sEnvironmentSystemalready hides authored backgrounds in an AR session, which is behaviour worth inheriting rather than fighting. A gradient's equator is derived from the same ramp the three.js adapter uses, so the two engines agree at the horizon and not only at the poles. - Lights -
AmbientLightComponentandDirectionalLightComponenton transform entities. The key entity is rotated so its local-Zruns along the direction the light travels. - Image-based lighting -
IBLGradientandIBLTexture.kind: "room"is native here: IWSDK's own component takes the string"room"for its built-in probe, so the one kind three.js has to approximate is the one this host has for free. - Authored skies -
DomeTexture, with blurriness and rotation. The gradient dome is removed rather than left underneath, so the two never fight over the background. - Depth occlusion -
DepthSensingSystemfor the system-level settings andDepthOccludableper entity. IWSDK opts entities in one at a time by patching their materials, soscope: "all"needs anoccludablesoption naming which entities the real world may hide - the entities are content, and content is yours. Whatever the port adds it remembers, so turning occlusion off never strips a component you added yourself. Without that option it reportsunavailableand says so. - Light estimation - none. IWSDK 0.5.3 has no WebXR light estimation at all, and the port reports
unsupportedwith that sentence rather than going quiet. Filed upstream as item 6.1. Anibl: { kind: "estimated" }asked for here is refused the same way, and the environment map you set yourself is left where it was. - The room - hits carry no distance here, because IWSDK moves a target entity to the hit and keeps the ray to itself; guessing from the head would be a different number wearing the same name.
IWSDKWorldSensingPortqueries the entities IWSDK'sSceneUnderstandingSystemalready created rather than reaching past it to the session, so planes, meshes and anchors come with IWSDK's own measurements. A hit test becomes an entity carryingEnvironmentRaycastTarget, which is how IWSDK casts; creating an anchor means creating an entity withXRAnchor, which is how IWSDK anchors. - Audio - one entity per voice, carrying
AudioSourcewithplaybackModepinned toOverlap, plus the cue's own reference distance, rolloff, maximum distance,DistanceModeland cone. IWSDK hands the entity transform to the same three.js positional audio the other adapter uses, so afacingturns the entity and the cone angles are written in degrees. The core has already applied the cue's retrigger policy, and letting IWSDK apply its own on top would makerestartmean two different things on two engines.
Every one of those is an IWSDK platform component with no app-side equivalent. The adapter creates no geometry: a floor would be a mesh, and meshes are the app's.
Fog is the exception. IWSDK has no fog component, so it is set on world.scene directly, with Fog / FogExp2 imported from three, a declared peer. Importing them through @iwsdk/core's export * from 'three' looked equivalent, but a consumer that excludes three from Vite's dependency optimizer cannot resolve names that only exist behind that star.
Knowing when a sound finished
IWSDK reports whether a source is playing, never that it has just stopped. So the port polls in the tick it is already given, and reports the end once a voice has been seen playing and then is not. A voice that never starts at all - a missing file, a failed decode - is reaped after startTimeoutMs (10 s by default) with a warning, rather than being tracked forever.
Passthrough
Push it in; do not expect the package to find it:
// `adapter` is the service framework's RuntimeAdapter; the flag it reports is
// derived from the LIVE session, not from what was requested.
adapter.onCapabilitiesChange((c) => env.environment.setPassthrough(c.passthrough));
A session belongs to the platform layer, not to the environment. setPassthrough(true) suppresses the sky and fog on top of whatever the app asked for, and turning it off restores exactly what was there - nothing has to remember what to put back.
Peer dependencies
@iwsdk/core >= 0.5.0 < 0.6.0, developed and tested against 0.5.3.
three >= 0.170.0, which every IWSDK application already carries (IWSDK projects alias it to super-three). The adapter imports three.js classes from three directly rather than through @iwsdk/core's star re-export, so it prebundles even when an application excludes three from Vite's dependency optimizer to transform its source.
Licence
MIT.
Classes
| Class | Description |
|---|---|
| AudioDirector | - |
| EnvironmentDirector | - |
| EnvironmentSystem | The per-frame tick. |
| IWSDKAudioPort | Plays sounds. |
| IWSDKEnvironmentPort | Applies an environment to a scene. |
| IWSDKWorldSensingPort | What an adapter implements. Everything except observe is optional, because every host has a different subset and a missing method is reported rather than thrown. |
| WorldSensingDirector | - |
Interfaces
| Interface | Description |
|---|---|
| AmbientLightSpec | Uniform illumination from every direction. |
| AudioCone | A directional sound, in this package's own terms. |
| AudioCue | A sound the app knows how to make. Registered once, played by id. |
| AudioDirectorOptions | - |
| AudioPort | Plays sounds. |
| AudioSpatial | How a positional voice gets quieter with distance. |
| AudioVoice | A sounding voice. Opaque to the app apart from the fields shown. |
| AudioVoiceRequest | A play request with every decision already made: which file, how loud in absolute terms, looping or not, and where from. An adapter implementing AudioPort has no mixing left to do. |
| EnvironmentDirectorOptions | - |
| EnvironmentPort | Applies an environment to a scene. |
| EnvironmentPortHost | What a port is handed so it can talk back. Implemented by the director. |
| EnvironmentSpec | A PARTIAL description of the environment. |
| EstimatedLighting | Lighting the host measured from the real world. |
| FogExponential | Fog that thickens exponentially with distance. |
| FogLinear | Fog that ramps between two distances. |
| HitTestRequest | A standing question: "where does this ray meet the room". |
| IblEstimated | The reflections the HOST measured from the real room. |
| IblGradient | Image-based lighting: what the world reflects and how it lights everything that is not lit by the key light. |
| IblRoom | The host's built-in neutral room probe. Neither authored nor measured. |
| IblTexture | @realitycollective/webxr-environment - the engine-free core. |
| IWSDKAudioPortOptions | - |
| IWSDKEnvironmentOptions | - |
| IWSDKEnvironmentPortOptions | - |
| IWSDKEnvironmentSetup | - |
| IWSDKWorldSensingPortOptions | - |
| KeyLightSpec | The one directional light most scenes need: a sun, a moon, a work lamp. |
| LightEstimationSpec | Which slots a live estimate may take over. |
| OcclusionSource | How the app would like the depth sensor configured. |
| OcclusionSpec | - |
| PlayOptions | Per-play overrides. Everything here beats the cue's own value. |
| ResolvedEnvironment | Every slot decided. This is what the director holds and adapters receive. |
| ResolvedLightEstimation | spec with every default filled in, which is what a port receives. |
| ResolvedWorldDetection | - |
| SensingReport | - |
| SkyGradient | A vertical gradient sky. top sits at the zenith and bottom at the nadir; horizon is where they meet, as a fraction of the way up the sphere. |
| SkySolid | A single flat colour behind everything. The cheapest sky there is. |
| SkyTexture | An authored sky: an equirectangular image wrapped around the world. |
| TransitionOptions | - |
| WorldAnchor | A point in the real world the runtime promises to keep track of. |
| WorldChange | What moved since the last flush, per feature. Ids only; read the lists. |
| WorldDetectionSpec | Which of them to turn on. Everything defaults OFF: each one costs. |
| WorldHit | Where a ray met the world. |
| WorldMesh | Scanned geometry: furniture, walls, whatever the room-scan produced. |
| WorldPlane | A flat surface the host found: a floor, a wall, a table top. |
| WorldPose | Where something is, in whatever reference space the app is using. |
| WorldSensingDirectorOptions | - |
| WorldSensingPort | What an adapter implements. Everything except observe is optional, because every host has a different subset and a missing method is reported rather than thrown. |
| WorldSensingPortHost | What a port is handed so it can report what the room looks like. |
Type Aliases
| Type Alias | Description |
|---|---|
| AudioBus | A mix group. Any string works; the conventional set is below. |
| CuePolicy | What a second play request does while a cue is already sounding. |
| EasingFunction | t in 0..1 -> eased t in 0..1. |
| EasingName | The named easing curves. A transition may also carry its own function. |
| EnvironmentBlendMode | How the host composites what is rendered over the real world. |
| EnvironmentListener | Fired whenever what the port holds, or what the app asked for, changes. |
| EnvironmentSlot | @realitycollective/webxr-environment - the engine-free core. |
| FogSpec | @realitycollective/webxr-environment - the engine-free core. |
| IblSpec | @realitycollective/webxr-environment - the engine-free core. |
| OcclusionMode | How hard the edge between real and virtual is drawn. |
| OcclusionScope | Which content the real world is allowed to hide. |
| Quat | A rotation as [x, y, z, w]. Normalised; the adapters guarantee that. |
| Rgb | An sRGB colour, one channel per component, each in 0..1. |
| SensingFeature | - |
| SensingListener | - |
| SensingState | How a feature is doing, in the order things usually go wrong: |
| SkySpec | @realitycollective/webxr-environment - the engine-free core. |
| Vec3 | A direction or position in world space, metres. Need not be normalised. |
| WorldChangeListener | - |
| WorldFeature | - |
| WorldPlaneOrientation | Roughly which way a surface faces. |
Variables
| Variable | Description |
|---|---|
| ALL_WORLD_DETECTION | Everything on: what setDetection(true) means. |
| DAWN | - |
| DEFAULT_BUS | The bus a cue lands on when it does not name one. |
| DEFAULT_BUSES | The buses a director starts with, each at unity gain. Any other name works too - a bus springs into existence at unity the first time it is named. master is NOT in here: it is a separate scalar over all buses, so that "duck everything" and "turn the music down" never fight over one number. |
| DEFAULT_LIGHT_ESTIMATION | - |
| DEFAULT_OCCLUSION | Soft edges over everything, from a GPU texture. |
| DUSK | - |
| EMPTY_ENVIRONMENT | An environment with nothing in it: a black void with no light. |
| ENVIRONMENT_SLOTS | The slot names, in the order the director pushes them to the port. |
| NIGHT | - |
| NOON | - |
| OVERCAST | - |
| SENSING_FEATURES | The sensor-backed features an adapter can report on. |
| STOCK_PRESETS | All of the above, ready to hand to EnvironmentDirector's presets. |
| VOID | Nothing at all: black, unlit, empty. The starting point of a void scene. |
| WORLD_FEATURES | The world features an app can ask for. They are named separately from the environment's because a host can have any subset: Quest has planes, meshes and anchors; a phone AR runtime may have only planes and hit test. |
Functions
| Function | Description |
|---|---|
| clamp | Clamp to an arbitrary range. |
| clamp01 | Clamp to the unit interval. |
| clearedFog | The same fog, pushed far enough away to be invisible. |
| deepEquals | Structural equality for the plain-data specs. |
| environmentFor | The setup registered for a world, if registerEnvironment has run. |
| interpolateEnvironment | Blend two resolved environments. t is the EASED progress in 0..1; the director has already applied the curve, so this stays linear per channel. |
| lerp | Linear interpolation. t is NOT clamped - the callers already clamp it. |
| lerpRgb | Component-wise colour interpolation. |
| registerEnvironment | One call to wire the environment (and optionally the audio) into an IWSDK world, and register the system that ticks them. |
| resolveEasing | Resolve an easing name or function to a function. Unknown names are linear. |
| resolveLightEstimation | - |
| resolveWorldDetection | - |
| rgbEquals | True when two colours are identical channel for channel. |
| rgbFromHex | 0xff8800 -> [1, 0.533…, 0]. Convenience for authoring presets. |
| rgbToHex | [1, 0.533…, 0] -> 0xff8800. The inverse, for adapters that want a hex. |
| unsupported | The report a feature has before any adapter has said otherwise. |
| worldEntryChanged | Has this entry changed since the last one with the same id? |