@urth/metatell-sdk - v0.1.0
    Preparing search index...

    Environment backgrounds

    Use /environment with a host-provided room scope to change the local user's background. Query getCapabilities().localKinds for the integrating host's supported background kinds; the SDK schema alone does not install a renderer. Lighting, reflection/environment maps, fog, and room geometry remain unchanged. Other participants do not receive these local changes.

    import { getCapabilities, setLocalOverride, onChanged } from "@urth/metatell-sdk/environment";
    import { waitUntilReady } from "@urth/metatell-sdk/runtime";
    import type { PluginRuntimeContext } from "@urth/metatell-sdk/lifecycle";

    async function showSky(sdk: PluginRuntimeContext) {
    const scope = sdk.getRoomScope();
    const capabilities = await getCapabilities({ scope });
    if (!capabilities.localKinds.includes("procedural-sky")) return;
    await waitUntilReady("environment", { scope });
    return setLocalOverride({
    mode: "custom",
    background: {
    kind: "procedural-sky",
    parameters: { azimuth: 0.495 },
    lighting: "preserve",
    },
    }, { scope });
    }

    function observeView(sdk: PluginRuntimeContext) {
    return onChanged((state) => {
    console.log(state.local, state.effective, state.render);
    }, { scope: sdk.getRoomScope() });
    }

    Handle rejection at your application boundary. observeView returns the UI's unsubscribe function: call it when that view closes, without disposing the plugin's room scope. The background continues until the owner explicitly releases it or the host invalidates that room/plugin lifetime. On a room change, subscribe using the new scope from sdk.onRoomChanged rather than reusing the old one.

    • One room scope exclusively owns the personal slot. A different scope attempting custom or scene-default receives CONFLICT; there is no automatic takeover.
    • custom loads and applies a background. scene-default displays the current scene default while retaining the owner's slot. follow-room releases only the calling scope's slot; a non-owner cannot clear another owner's background.
    • follow-room displays the newest accepted shared snapshot, or the scene default if no shared background exists. A personal custom or scene-default slot remains unchanged while shared snapshots update behind it.
    • Durable sharing is negotiated separately as capabilities.shared. Older servers continue reporting false; installing a local renderer does not enable sharing. Never use the host-local scene counter as an authoritative backend revision.
    • UI close/reopen does not release the room scope. Plugin removal/replacement, runtime exit, or room/scene change aborts it and frees its local resources.
    • Requests are relative to the exact room ID, scene revision, and generation. Old scopes reject with ABORTED or STALE_SCENE and cannot clear new owners.
    await setLocalOverride({ mode: "scene-default" }, { scope });
    await setLocalOverride({ mode: "follow-room" }, { scope });

    scope above is the current PluginRoomScope.

    getState({ scope }) and onChanged(callback, { scope }) return copied, owner-relative snapshots. local.requested and local.ownsSlot describe the calling owner, while effective describes the currently applied visual. render.status is loading, ready, or failed, with a fixed error code. Here ready means preparation and local scene activation succeeded; it is not an acknowledgement of the first GPU frame, context recovery, or another client. Other owners do not receive the holder's private owner ID or requested override. The effective background is room-local visible state, not secret data.

    Preparation leaves the preceding visual displayed. Only a successful preparation and activation replace it. Failure preserves the preceding visual and reports ASSET_FAILED; the owner retains its slot to retry or release it. A newer request by the same owner cancels older preparation. A cancelled or timed-out preparation cannot later activate, but cancellation does not undo an already accepted visual. Cancellation is not a rollback guarantee for accepted server writes.

    getCapabilities is available once the driver is installed, including before connection/readiness. State reads and acquisitions require a connected, ready room. Release remains available while disconnected or loading. An accepted local background stays visible during a temporary disconnect in the same room. onChanged coalesces state reads and rejects old epochs/revisions; use getState when an explicit error is needed, since observer read failures are not callbacks.

    Errors include UNSUPPORTED, NOT_READY, DISCONNECTED, CONFLICT, ABORTED, TIMEOUT, INVALID_ARGUMENT, STALE_SCENE, ASSET_FAILED, and INTERNAL. An older host rejects immediately with UNSUPPORTED. Request timeoutMs defaults to 5000; override it explicitly for larger image loads. No automatic retry occurs.

    import { getState, updateShared } from "@urth/metatell-sdk/environment";

    const current = await getState({ scope });
    if (current.shared.supported && current.shared.snapshot) {
    const accepted = await updateShared({
    requestId: crypto.randomUUID(),
    expectedSceneRevision: current.shared.snapshot.sceneRevision,
    expectedRevision: current.shared.snapshot.revision,
    background: { kind: "color", color: "#223344", lighting: "preserve" },
    }, { scope });
    console.log(accepted.revision); // Server acceptance, not render completion.
    }

    The authenticated server checks current update_hub permission and atomically compares the explicit server scene/revision with persisted room state. A scene mismatch rejects with STALE_SCENE; a competing write rejects with CONFLICT. Pass background: null to clear the durable room setting. Scope disposal releases only personal resources; it never deletes the server's persisted setting.

    requestId is caller-owned, separate from the SDK's RPC correlation ID. The server stores an idempotency receipt keyed by room, authenticated actor and request ID. An explicit retry must use the same request ID and identical payload, including expected revisions, to retrieve that acceptance. Reusing it for different content returns CONFLICT; current authorization is checked even for a replay. A replay may return an older accepted snapshot while the current room has advanced. The SDK returns that ACK without rolling current shared state back. There are no automatic mutation retries. If a write times out or is aborted, it may already have committed; inspect a fresh snapshot and apply the server's receipt policy before deciding to retry.

    Request IDs are 1–128 UTF-8 bytes, expected scene revisions 1–256 UTF-8 bytes; NUL and invalid Unicode are rejected. Revisions are nonnegative safe integers. Shared URLs are normalized first, then restricted to the backend-compatible credentialless HTTPS subset. HTTP localhost exceptions are personal only.

    state.shared.snapshot is null until the host binds a fresh server snapshot to the currently rendered scene, including after reconnect. During that interval, updateShared rejects with NOT_READY. Server scene revisions are opaque IDs, independent from scope.room.sceneRevision. Older/out-of-order snapshots and assets finishing after scene invalidation cannot replace newer visuals.

    A successful updateShared returns the server's SharedEnvironmentSnapshot as soon as it is acknowledged; rendering may still be loading or failed. Observe state.render separately. A personal override can intentionally hide the shared setting. Render failures retain the preceding visual without rolling back the durable server setting. This SDK contract does not prove deployment, database migration, another client's display, or delivery of a first GPU frame.

    All kinds require lighting: "preserve". from-background is unsupported.

    Kind Fields and units
    color color: "#RRGGBB" in sRGB. No named colors or alpha.
    equirectangular Absolute url, projection: "equirectangular", colorSpace: "srgb"; raster image with exactly 2:1 dimensions.
    procedural-sky Optional parameters use the defaults and ranges below. Values are finite; out-of-range inputs are rejected, not clamped.
    model URL and position/rotation/positive scale vectors. Position uses host world units; rotation uses XYZ Euler angles in radians. Requires model in localKinds; hosts without that renderer reject it as UNSUPPORTED. Asset format and complexity limits depend on the installed host driver.
    Sky parameter Default Inclusive range
    turbidity 10 0 to 20
    rayleigh 2 0 to 4
    luminance 1 0 to 2
    mieCoefficient 0.005 0 to 0.1
    mieDirectionalG 0.8 0 to 0.999
    inclination 0 -0.5 to 0.5
    azimuth 0.15 0 to 1
    distance 8000 1 to 10000 world units (sky cube edge length)

    The normalized angle parameters follow the existing shader convention: theta = pi * (inclination - 0.5), phi = 2*pi * (azimuth - 0.5); the sun direction is normalized (cos(phi), sin(phi)*sin(theta), sin(phi)*cos(theta)). They are not geographic latitude/longitude or degrees. The SDK-owned mesh uses the specified distance without the existing A-Frame component's internal clamp. The original component is unchanged.

    URLs must use HTTPS and contain no credentials; HTTP is accepted only for localhost or 127.0.0.1 development. The URL limit is 4096 characters. data:, blob:, file:, and SVG are rejected. Images must have PNG/JPEG/WebP MIME and matching byte signatures, be accessible by CORS, and require no host credentials. Fetch uses credentials: "omit"; the SDK never attaches user tokens. Use a suitable public or explicitly signed asset URL.

    The driver streams at most 16 MiB of compressed bytes and checks image dimensions before creating the GPU texture. The maximum is 8192x4096, further bounded by the renderer texture limit; without that information it uses 4096x2048. Query the actual advertised limits. These are implementation bounds, not a claim of performance across every supported device. Browser decoding allocates image memory before the decoded-size check; this is not an untrusted-image sandbox.

    DEFAULT_SKY_PARAMETERS and DEFAULT_ENVIRONMENT_LIMITS expose the defaults above. The copy… and validateEnvironmentRequest functions are the validators the SDK uses at the bus boundary; host integrations can reuse them. They throw SdkError on invalid input.

    The host hides only existing skybox meshes while an override is active, preserving their associated lighting. Procedural sky uses the pure shader object without A-Frame light-probe/ambient generation. Only owned textures, ImageBitmaps, materials, and cloned geometry are disposed. Scene-owned resources are retained. Scene invalidation releases the old visual before the next default is applied.

    This API uses the trusted same-window plugin bus. Ownership supports predictable cleanup; it is not isolation from arbitrary JavaScript on the host page.