PRO

Video Summagator

A clip turned into a solid you can orbit. The video is sampled into 96, 160 or 240 evenly spaced frames and stacked along Z as one Data3DTexture, so width and height stay the picture while depth becomes running time. A GLSL3 ray-marcher reads that cube under an orthographic camera: parallel rays, so the origin is projected back onto the camera plane rather than taken from the camera position. The selected frame splits the volume in two, everything after it opaque and everything before it accumulating over 160 steps, which is why scrubbing slides a solid surface through a smear instead of dissolving it. That surface gets a 1px outline measured in CSS pixels from four projected corners, so it holds its weight at any zoom. Viewers can drop in their own mp4 and it never leaves the browser: frames are seeked, drawn to a canvas and read back on-device. It ships no control panel of its own: every live value is a prop and every one-shot action is a method on the ref, so the controls live wherever the host puts them.

Component
SPACE / TIME STUDY

Video Summagator

A moving image, unfolded into time.

0.00s
TIME →
SELECTED FRAME00:00.00

Drag to orbit · Scroll to zoom · ← → step through time · Space to play

Install
npx shadcn@latest add @compronents/video-summagator

Installs from ui.aryank.space. Needs a registry token and a one-time @compronents entry in components.json. The docs walk through both.

Files
Checking access…
API
PropTypeDefaultDescription
srcstringthe hosted sample clipClip loaded on mount. It is drawn to a canvas and read back, so it must be same-origin or CORS-readable; a cross-origin clip without CORS headers taints the canvas and the import fails.
sampleLabelstring"Flowers · CC0 sample"Name shown for the built-in clip in the panel readout.
eyebrowstring"SPACE / TIME STUDY"Small tracked line above the title.
titlestring"Video Summagator"Heading in the top-left corner.
taglinestring"A moving image, unfolded into time."Line under the heading. Hidden at compact widths.
quality96 | 160 | 240160Frames sampled out of the clip. Changing it re-decodes, which is one serial seek per frame.
playingbooleanfalseRuns the playhead. The component reports it back through onPlayingChange when a keyboard scrub or a new clip stops playback.
speednumber1Playback rate while playing.
depthnumber3Depth of the volume in world units: how far time is stretched along Z.
densitynumber0.1Extinction of the transparent region before the selected frame. Low values smear, high values read as a slab.
brightnessnumber1Multiplier applied after the sRGB decode in the shader.
showFramebooleantrueDraws the 1px outline around the selected frame.
autoRotatebooleanfalseOrbits the camera at 0.6 rad/s.
onTimeChange(time: number, duration: number) => void—Fires at most every 100ms while the playhead moves, so a host scrubber can follow playback without re-rendering per frame.
onReady(info: VolumeInfo) => void—Fires when a clip finishes decoding, with the sample count, frame size and byte total.
onMessage(message: string | null) => void—Decode failures, cancellations and WebGL trouble. Null clears the current message.
onPlayingChange(playing: boolean) => void—Fires when playback should stop, e.g. after a keyboard scrub or a new clip load.
onBusyChange(busy: boolean) => void—True while a clip is decoding.
refRef<VideoSummagatorHandle>—One-shot actions: seek(seconds), stepFrame(1 | -1), resetCamera() and frontView().
classNamestring—Extra class on the root element.
Customize
titletext

Heading in the top-left corner.

taglinetext

Line under the heading.

srcasset-url

Clip sampled into the volume on load.

Dependencies
three