← Back to catalog
shader-transition / chromatic-split
chromatic-split
Separates color channels across the transition. Ported from the pinned Hyperframes shader.
Use this effect
VideoML XML
Save this example as example.videoml.xml and place the downloaded asset in an assets/ directory beside it. Run vml pipeline example.videoml.xml with a VideoML installation that supports frame effects.
Download Geist-Latin.woff2 to assets/.
<vml id="catalog-example" width="1280" height="720" fps="30" duration="4s">
<voiceover seed="0"/>
<assets><asset id="font" src="assets/Geist-Latin.woff2" media-type="font/woff2"/></assets>
<scene id="from" start="0s" duration="3s">
<video-background props='{"color":"#224466"}'/>
<frame-effect id="echo" effect-id="videoml/echo-smear" effect-version="1" props='{"parameters":{"length":100,"threshold":40,"color":"#71f5a7"}}' start="0s" duration="3s"/>
</scene>
<transition id="transition" effect="chromatic-split" start="2s" duration="1s" ease="linear" props='{"accent":[1,0.6,0.2],"dark":[0.4,0.15,0],"bright":[1,0.85,0.5]}'/>
<scene id="to" start="2s" duration="2s">
<video-background props='{"color":"#88aa66"}'/>
<frame-effect id="particles" effect-id="videoml/particle-text-dissolve" effect-version="1" props='{"parameters":{"text":"VideoML","density":"med","accent":"green","direction":"in"}}' start="0s" duration="2s"/>
</scene>
</vml>
TypeScript
Requires the frozen stdlib package, GSAP, a page element with id="effect-root", and the referenced local assets. Revoke any generated object URLs when the host disposes the effect.
import { createEffectRegistry, mountEffect } from '@videoml/stdlib/effects';
import { registerEchoSmear } from '@videoml/stdlib/effects/echo-smear';
import { registerParticleEffects } from '@videoml/stdlib/effects/particles';
import { mountSceneCompositor } from '@videoml/stdlib/effects/transitions';
import { gsap } from 'gsap';
const root = document.querySelector<HTMLElement>('#effect-root');
if (!root) throw new Error('Missing #effect-root');
const registry = createEffectRegistry(); registerEchoSmear(registry); registerParticleEffects(registry);
const response = await fetch('./assets/Geist-Latin.woff2');
if (!response.ok) throw new Error('Font unavailable');
const font = URL.createObjectURL(await response.blob());
const context = { width: 1280, height: 720, fps: 30, seed: 0, assets: { font } };
function scene(id: string, parameters: Record<string, unknown>, startFrame: number, endFrame: number, background: string) {
const layer = document.createElement('div'); Object.assign(layer.style, { position: 'absolute', left: '-10000px', top: '0', width: '1280px', height: '720px' }); document.body.append(layer);
const effect = mountEffect(layer, registry, { id, version: '1', parameters, startFrame, endFrame }, context);
const canvas = document.createElement('canvas'); canvas.width = 1280; canvas.height = 720;
const ctx = canvas.getContext('2d'); if (!ctx) throw new Error('Canvas unavailable');
return { seek: (frame: number) => effect.seek(frame), async capture() { const content = layer.querySelector('canvas'); if (!content) throw new Error('Scene unavailable'); ctx.fillStyle = background; ctx.fillRect(0, 0, 1280, 720); ctx.drawImage(content, 0, 0); return canvas; }, dispose() { effect.dispose(); layer.remove(); } };
}
const from = scene('videoml/echo-smear', {"length":100,"threshold":40,"color":"#71f5a7"}, 0, 90, '#224466');
const to = scene('videoml/particle-text-dissolve', {"text":"VideoML","density":"med","accent":"green","direction":"in"}, 60, 120, '#88aa66');
const easeName = "linear";
if (!/^[A-Za-z][A-Za-z0-9]*(?:\.(?:in|out|inOut))?$/.test(easeName)) throw new Error('TRANSITION_INVALID: unknown easing');
const ease = gsap.parseEase(easeName); if (typeof ease !== 'function') throw new Error('TRANSITION_INVALID: unknown easing');
const transition = mountSceneCompositor(root, { from, to, width: 1280, height: 720, startFrame: 60, endFrame: 90, shader: "chromatic-split", parameters: {"accent":[1,0.6,0.2],"dark":[0.4,0.15,0],"bright":[1,0.85,0.5]}, ease });
await transition.seek(75);
// On teardown: transition.dispose(); URL.revokeObjectURL(font);Upstream Apache-2.0 license · GSAP license · Geist font license
implemented · interactive preview
This entry is implemented but not yet verified.