Skip to content

Video

Plays a video file as a texture on a quad. Supports green-screen keying (isGreenScreen, backgroundColor, similarity, smoothness, spill). The video element, texture, and material are runtime-only and re-created when the project loads.

Managed by: VideoSystem

Editor inspector

Video inspector

Properties

PropertyTypeDescription
pathstringSource URL or localasset:// path of the video file.
optionstypeof DEFAULT_VIDEO_OPTIONSPlayback + green-screen options.
objectTHREE.Object3D | nullLive three.js object hosting the video quad. null until built.
videoElementHTMLVideoElement | nullUnderlying HTML video element. null until built.
textureTHREE.VideoTexture | nullThree.js texture sampling the video element. null until built.
materialTHREE.Material | nullMaterial rendering the video texture (with green-screen shader when enabled). null until built.
audioNodeTHREE.Audio<GainNode> | THREE.PositionalAudio | nullTHREE.js audio node connected to this video's audio stream via setMediaElementSource. Present only in preview when the camera listener is available. null in editor mode.
isPlayingbooleanWhether the video should be playing. Managed by VideoSystem in preview. Non-enumerable — not persisted to snapshots or save data. Set via play / stop.
isPausedbooleanWhether the video is paused mid-playback (position is preserved). Non-enumerable — not persisted to snapshots or save data. Set via pause / play / stop.

API reference

Scripting examples

Component access patterns

Use this.getComponent(X) to access a component on the entity this script is attached to. Use this.world.getEntityByName to find another entity by name, then this.world.getComponent to read its component:

typescript
// Component on the entity this script is on
const video = this.getComponent(VideoComponent);

// Component on another entity, found by its scene name
const other = this.world.getEntityByName("Screen");
if (other) {
  const video = this.world.getComponent(other.entityId, VideoComponent);
}

Play / pause on key press

Toggle video playback when the player presses Space.

typescript
import { Behaviour, VideoComponent, KeyboardKeys } from "@relu-interactives/spatial-ecs";

export default class VideoController extends Behaviour {
  protected onUpdate() {
    const video = this.getComponent(VideoComponent);
    if (!video) return;

    if (this.world.getKeyDown(KeyboardKeys.Space)) {
      if (video.isPlaying) {
        video.pause();
      } else {
        video.play();
      }
    }
  }
}

Inspector-driven volume and loop

Expose volume and loop settings as inspector fields so they can be tuned without re-entering the code.

typescript
import { Behaviour, VideoComponent } from "@relu-interactives/spatial-ecs";
import type { FloatInput, BooleanInput } from "@relu-interactives/spatial-ecs";

export default class VideoSettings extends Behaviour {
  data = {
    volume: 1 as FloatInput,
    loop: true as BooleanInput,
  };

  protected onUpdate() {
    const video = this.getComponent(VideoComponent);
    if (!video) return;
    video.options.volume = this.data.volume;
    video.options.loop = this.data.loop;
  }
}

Via ComponentInput in the inspector

You can also pick this component from the inspector using a ComponentInput field. Assign any entity in the inspector and the field resolves to the live VideoComponent instance at runtime.

typescript
import {
  Behaviour,
  VideoComponent,
  type ComponentInput,
} from "@relu-interactives/spatial-ecs";

export default class Example extends Behaviour {
  data = {
    targetVideo: {
      type: "component",
      value: null,
    } as unknown as ComponentInput,
  };

  protected onUpdate() {
    const video = this.data.targetVideo.value as VideoComponent | null;
    if (!video) return;
    // Use the live component instance.
  }
}

Via EntityInput in the inspector

Alternatively, pick an entity from the inspector using an EntityInput field and then read the component off that entity via world.getComponent:

typescript
import {
  Behaviour,
  VideoComponent,
  type EntityInput,
  type WorldEntityView,
} from "@relu-interactives/spatial-ecs";

export default class Example extends Behaviour {
  data = {
    targetEntity: { type: "entity", value: null } as unknown as EntityInput,
  };

  protected onUpdate() {
    const entity = this.data.targetEntity.value as WorldEntityView | null;
    if (!entity) return;

    const video = this.world.getComponent(
      entity.entityId,
      VideoComponent,
    ) as VideoComponent | null;
    if (!video) return;
    // Use the component on the picked entity.
  }
}

Playback methods

Use video.play(), video.pause(), and video.stop() to control playback. These update isPlaying / isPaused so the VideoSystem applies the correct HTML video element call in preview.