video()

Purpose: Spawn and control HTML5 video overlays during a score.

Required

File Resolution

Videos are resolved in order:

  1. Project video directory: scores/<project>/video/<filename>
  2. Shared video directory: shared/video/<filename>

If the file is found in neither, the console logs an error naming both paths that were tried — check there first when a video cue "does nothing". Filenames are case-sensitive on the server: Lum.mp4 will not match lum.mp4.

Getting the file into the project

The DSL editor's video file field has a Browse… button: pick any video on your system and it is copied into the project's video/ folder and the field filled in — no manual file management. If a file with the same name already exists there, the existing file is referenced instead.

Placement & Positioning

Size

Audio (Default Muted)

Spawning / Reuse

Timing & Playback

Glitch grains — in:, seg:, speed: as lists or patterns

The same forms as audio(): in: a list or pattern gives several start points, seg: is the length of each grain from its start point, speed: a pattern gives each grain its own rate. All three are drawn per grain on the single video element — a seek, never a re-create — and loop: counts grains (0 = endless):

video(file:clip, size:fs, fit:cover, in:Prand(2, 9, 15, 31, inf), seg:0.4, speed:Prand(0.5, 1, 2, inf), loop:60)
video(file:clip, w:640, h:360, in:[2, 9, 15], seg:Pseq(0.4, 0.4, 0.8, inf), loop:0)

Two limits, both the browser's: no backwards grains (negative playbackRate is unsupported for <video> — negative speeds play forwards), and seek latency is set by the file's keyframe spacing — a normal encode lands each jump on the nearest keyframe and decodes forward, which stutters at grain rates above ~1–2 Hz. For fast grains make an all-intra copy, where every frame is a keyframe:

ffmpeg -i clip.mp4 -c:v libx264 -x264-params keyint=1:min-keyint=1 -crf 23 -an clip_glitch.mp4

(demo-video's "Glitch" box uses lum_glitch.mp4, a 10 s all-intra excerpt.)

Modal / UI Features

Triggering

All of these — and every other parameter on this page — are editable as form fields in the DSL editor (click the element with the inspector active); new video cues start prefilled with trig:playhead, w:480, h:270, fadeIn:0.5.

Interaction

Removal


Video on the control plane

A video reports where it has got to, and can be driven while it plays:

Channel Direction Meaning
time out playback position in seconds
t out through the file, 0–1
opacity in 0–1
speed in playback rate
video(uid:film, file:lum, w:640, h:360, speed: follow(fader.t, 0.5, 2))
rotate(uid:r1, dir:1, dur: follow(film.t, 10, 1))

speed is clamped to what browsers actually accept (0.0625–16) and made positive — a <video> cannot play backwards. A bound opacity owns the value: fadeIn/fadeOut are a timed ramp on the same property, so the ramp is switched off rather than left to fight the signal.

Position is reported per decoded frame where the browser supports it, falling back to about four times a second — and the slower path keeps reporting in a background tab, where the per-frame one stops.

Until now a follow() written on a video() was silently ignored: video builds its parameters differently from every other cue, and the conversion that turns follow(...) into a binding never ran on them.

Demo Project

The bundled demo-video project walks through every feature on one score: windowed, modal, and fullscreen spawning, size variants, audio, speed/opacity, looping, trim + timed removal, concurrent instances (new:1), target anchoring with location:scroll, follow:1 on a trans()-driven frame, and instance reuse (hot-updating a running video by retriggering the same file + target).

Examples

  1. Fullscreen, pass-through clicks, but allow click-to-close
    cue:video(file:intro.mp4,size:fs,clickable:1,audio:1,fadeIn:0.5)
    

1b. Fullscreen backdrop that fills the screen edge-to-edge (no bars)

video(file:atmosphere.mp4, size:fs, fit:cover, loop:0, fadeIn:2, closeBtn:0)
  1. Windowed, anchored to a target, follows scroll, infinite loop
    cue:video(file:clip.webm,target:markerA,location:scroll,size:640x360,loop:0,opacity:0.9)
    

2b. Windowed, riding a target that trans() is moving

cue:video(file:clip.webm,target:carrier,location:scroll,w:320,h:180,follow:1,hold:12)

carrier is the inner rect; the animated group around it carries trans(…) in data-oscilla. The group's translate moves the frame and follow:1 keeps the video on it. The video is sized in score units (w:/h: under location:scroll), so it fills the frame exactly.

  1. Spawn a second independent instance via uid

    cue:video(file:cam.mp4,uid:stageLeft,in:5,fadeIn:1,fadeOut:1,hold:20)
    
  2. Force new instance without specifying uid

    cue:video(file:teaser.mp4,new:1,in:2,out:10,fadeIn:0.5,fadeOut:0.5,speed:1.25)
    
  3. Modal video with close button and backdrop

    cue:video(file:tutorial.mp4,w:960,h:540,location:fixed,closeBtn:1,backdrop:1,controls:1,fadeIn:0.3)
    
  4. Clickable SVG element that triggers a modal video

    Use button() with mode:overlay to make an SVG element trigger a video while keeping the SVG visible:

    <g id="button(trigger:video(file:tutorial.mp4,w:960,h:540,location:fixed,closeBtn:1,backdrop:1),mode:overlay,tooltip:Tutorial)">
      <rect x="0" y="0" width="50" height="48" rx="4" style="fill:#4a90d9"/>
      <text x="25" y="35" style="text-anchor:middle;fill:#fff">?</text>
    </g>
    

Note: size:fs always positions at (0,0) and ignores target geometry; all other sizes center on target (or the cue position) with optional offsetX/offsetY. Default audio is muted; use audio:1 to unmute. By default, same file+target cues reuse the existing element unless uid or new:1 is supplied.

Tip: use ← → or ↑ ↓ to navigate the docs