// scenes.jsx — PLUR demo storyboard scenes // Two agents: Data (gold #E5C07B) on the left, Quark (magenta #C678DD) on the right. // Narrative beats: Intro → Setup → Quark sells → Data fails → Exchange (commit-reveal skip) → Data succeeds → Payoff → Outro // ─── Small utility components ─────────────────────────────────────────────── function Kicker({ children, color = '#9ca3af', size = 16 }) { return (
{children}
); } function Chip({ children, color = '#fff', bg = 'rgba(255,255,255,0.08)', border }) { return ( {children} ); } // Fade/slide reveal container used by scene-internal elements. function Reveal({ at = 0, dur = 0.4, dy = 12, ease = Easing.easeOutCubic, children, style }) { const { localTime } = useSprite(); const t = ease(clamp((localTime - at) / dur, 0, 1)); return (
{children}
); } // Typed-terminal line: reveals one char per frame based on localTime. // Evidence registry + token detection — render clickable verification chips // inline inside terminal lines. Tokens: // 0x[0-9a-f]{8,} → Etherscan tx (Sepolia) // escrow # → DataEscrow contract URL (Sepolia) // Swarm: → Swarm gateway URL // mr-data@10... / quark@10... → agent card chip (non-link, informational) const EVIDENCE = { network: 'sepolia', escrowContract: '0x1111111111111111111111111111111111111111', // placeholder — replace with real DataEscrow address addresses: { 'mr-data': { label: 'mr-data', address: '0x42aa420000000000000000000000000000000042' }, 'quark': { label: 'quark', address: '0x7373730000000000000000000000000000000073' }, }, }; const etherscanTx = (hash) => `https://sepolia.etherscan.io/tx/${hash}`; const etherscanAddr = (addr) => `https://sepolia.etherscan.io/address/${addr}`; const swarmGateway = (ref) => `https://api.gateway.ethswarm.org/bzz/${ref.replace(/[…\.]+$/,'')}`; // Parse a terminal line's text and return React children with inline links // for tx hashes, escrow ids, Swarm refs. Caret handling is kept in the host. function renderWithEvidence(text, { color, linkColor }) { // Patterns in order — we'll tokenize by matching left-to-right. const patterns = [ { re: /0x[0-9a-fA-F]{16,}/g, kind: 'tx' }, { re: /0x[0-9a-fA-F]{8,15}/g, kind: 'tx' }, // short/truncated hashes { re: /escrow\s*#\d+/gi, kind: 'escrow' }, { re: /Escrow\s*#\d+/g, kind: 'escrow' }, { re: /(?:Swarm:\s*|bzz:0x)?[a-f0-9]{16,}…?/g, kind: 'swarm', guard: /[a-f0-9]{16,}/ }, ]; // Simpler: scan for the first match of each and merge positions. const out = []; let cursor = 0; // Combined regex — order matters. Keep tx first so it doesn't get swallowed. const combined = /(0x[0-9a-fA-F]{8,})|((?:[Ee]scrow\s*#\d+))|(Swarm:\s*[a-f0-9]{6,}…?)/g; let m; while ((m = combined.exec(text)) !== null) { const before = text.slice(cursor, m.index); if (before) out.push(before); const raw = m[0]; let href = null; if (m[1]) { href = etherscanTx(m[1]); } else if (m[2]) { href = etherscanAddr(EVIDENCE.escrowContract); } else if (m[3]) { const ref = m[3].replace(/^Swarm:\s*/, ''); href = swarmGateway(ref); } out.push( {raw} ); cursor = m.index + raw.length; } const tail = text.slice(cursor); if (tail) out.push(tail); return out.length ? out : [text]; } function TerminalLine({ text, startAt = 0, cps = 40, prefix = '', prefixColor, color = '#e5e7eb', bold = false, linkColor }) { const { localTime } = useSprite(); const hasStarted = localTime >= startAt; const elapsed = Math.max(0, localTime - startAt); const chars = Math.floor(elapsed * cps); const shown = text.slice(0, chars); const done = chars >= text.length; const caret = !done && hasStarted; // Only render evidence chips once the line has fully typed — otherwise // partial hashes would blink in-and-out as underlined links. const children = done ? renderWithEvidence(shown, { color, linkColor: linkColor || color }) : [shown]; // Do not render the line (including its prefix) before its startAt time — // otherwise empty `quark>` / `mr-data>` prompts ghost into the terminal // ahead of when they should appear. if (!hasStarted) return null; return (
{prefix && {prefix}} {children} {caret && }
); } // A whole terminal window with MOTD header describing the agent. function Terminal({ motd, agentColor, lines, style, width, height, startAt = 0 }) { return (
{motd?.host}
{/* MOTD */} {motd && (
{motd.name}
{motd.sub}
{motd.ip} · {motd.gpu} · QVAC runtime · PLUR memory
)}
{lines.map((ln, i) => ( ))}
); } // PLUR logo — official SVG mark (transparent background, works on dark). function PlurMark({ color, size = 40, palette }) { return PLUR; } // Video-inset picture-in-picture — shows real recording alongside mock terminals. // Controlled by parent (we forward currentTime) so it stays in sync with the scrubber. function VideoInset({ src, x, y, w, h, startSec = 0, muted = true, label = 'ORIGINAL RECORDING', color, parentLocalTime, sceneStart, watchUrl }) { const ref = React.useRef(null); const [hover, setHover] = React.useState(false); const [ready, setReady] = React.useState(false); // Seek + arm on metadata load — if the video isn't fully loaded yet, the // initial `currentTime = startSec` jump is ignored and we get a black frame. React.useEffect(() => { const v = ref.current; if (!v) return; const onReady = () => { setReady(true); const target = startSec + Math.max(0, parentLocalTime); try { v.currentTime = Math.min(target, (v.duration || 1e6) - 0.1); } catch {} v.play && v.play().catch(() => {}); }; if (v.readyState >= 1) onReady(); v.addEventListener('loadedmetadata', onReady); v.addEventListener('canplay', onReady); return () => { v.removeEventListener('loadedmetadata', onReady); v.removeEventListener('canplay', onReady); }; }, []); // Keep video in sync with the stage scrubber. React.useEffect(() => { const v = ref.current; if (!v || !ready) return; const target = startSec + Math.max(0, parentLocalTime); // Only correct if drift > 0.25s; otherwise let it play naturally. if (Math.abs((v.currentTime || 0) - target) > 0.25) { try { v.currentTime = target; } catch {} } v.play && v.play().catch(() => {}); }, [parentLocalTime, startSec, ready]); const content = (
watchUrl && setHover(true)} onMouseLeave={() => setHover(false)} >
); if (watchUrl) { return ( {content} ); } return content; } function formatTc(sec) { const m = Math.floor(sec / 60); const s = Math.floor(sec % 60); return `${String(m).padStart(2,'0')}:${String(s).padStart(2,'0')}`; } // Small brand lockup used on intro/outro. function BrandLockup({ tech, color, size = 13 }) { return (
{tech.map((t, i) => ( {t} {i < tech.length - 1 && /} ))}
); } // ─── Scene 1: Intro ───────────────────────────────────────────────────────── function IntroScene({ start, end, config }) { return ( {({ localTime }) => { const { bg, ink, dim, gold, magenta } = config; const gridX = (localTime * 8) % 60; return (
{/* Grid */}
{/* Two agent glows */}
plur.ai ↗
Sovereign minds.
Sovereign rails.
{(config.introSubtitle || '').split('\n').map((line, i) => ( {i > 0 &&
} {line}
))}
LIVE ON SEPOLIA
); }} ); } function PlurMarkOLD_REMOVED({ color, size = 36 }) { return ( ); } // ─── Scene 2: Setup — meet the two agents ─────────────────────────────────── function SetupScene({ start, end, config }) { return ( {() => { const { bg, ink, dim, gold, magenta } = config; return (
The cast
Two agents. Two machines.
Both running 32B models locally.
Different owner. Different session. No shared memory.
ENGRAM
A unit of learned memory.{' '} Portable. Installable. Tradeable.
); }}
); } function AgentCard({ at, color, name, role, quote, ip, gpu, trait, config }) { return (
{ip} · {gpu}
{name}
{role}
"{quote}"
{trait}
); } // ─── Scene 3: Terminal scene with overlays ────────────────────────────────── function TerminalScene({ start, end, config, step, title, subtitle, leftLines, rightLines, leftMotd, rightMotd, highlight, activeSide, evidence, evidenceShowAt = 3, videoSrc, videoStart, videoLabel, videoHref }) { return ( {({ localTime, duration }) => { const { bg, ink, dim, gold, magenta } = config; // Chapter marker band animation const markerIn = clamp(localTime / 0.5, 0, 1); const markerOut = clamp((duration - localTime - 0.4) / 0.4, 0, 1); const markerOpacity = Easing.easeOutCubic(markerIn) * Easing.easeOutCubic(markerOut); const stepColor = activeSide === 'left' ? gold : activeSide === 'right' ? magenta : ink; return (
{/* Chapter marker band — top */}
BEAT {step}
{title}
{subtitle}
{/* Two terminals */}
{highlight && (Array.isArray(highlight) ? highlight : [highlight]).map((h, i) => ( localTime > h.showAt ? ( ) : null ))} {videoSrc && (() => { // Sit the video inset over the IDLE terminal (opposite of activeSide) // so it never occludes the agent currently typing. // activeSide='right' (Quark typing) → inset goes LEFT (over Data's idle) // activeSide='left' (Data typing) → inset goes RIGHT (over Quark's idle) const insetW = 480, insetH = 270; const insetY = 1080 - insetH - 170; const insetX = activeSide === 'right' ? 70 // left : 1920 - insetW - 70; // right (default) return ( ); })()} {evidence && evidence.length > 0 && ( )}
); }}
); } function VerifyBarInner({ evidence, config, localTime, showAt }) { const o = clamp((localTime - showAt) / 0.5, 0, 1); if (o <= 0) return null; return (
Verify on-chain ↗ {evidence.map((e, i) => ( {e.label} ))}
); } function VerifyBar({ evidence, config, showAt = 3 }) { const { localTime } = useSprite(); const o = clamp((localTime - showAt) / 0.5, 0, 1); if (o <= 0) return null; return (
Verify ↗ {evidence.map((e, i) => ( {e.label} ))}
); } function CalloutBox({ showAt, hideAt, x, y, w = 420, label, note, side = 'right', color, parentLocalTime, config }) { const local = parentLocalTime - showAt; const total = (hideAt ?? (showAt + 2.5)) - showAt; const entryDur = 0.35; const exitDur = 0.3; const exitStart = total - exitDur; let o = 1, dx = 0; if (local < entryDur) { const t = Easing.easeOutBack(clamp(local / entryDur, 0, 1)); o = clamp(local / entryDur, 0, 1); dx = (1 - t) * (side === 'right' ? -16 : 16); } else if (local > exitStart) { const t = clamp((local - exitStart) / exitDur, 0, 1); o = 1 - t; dx = t * (side === 'right' ? 12 : -12); } if (local < 0 || local > total) return null; const c = color || config.gold; return (
{label}
{note}
); } // ─── Scene 4: Time-skip card for the commit-reveal delay ──────────────────── function TimeSkipScene({ start, end, config, skippedLabel }) { return ( {({ localTime, duration }) => { const { bg, ink, dim, gold, magenta } = config; const progress = clamp(localTime / Math.max(0.01, duration - 0.4), 0, 1); return (
{/* Scan lines */}
{Array.from({ length: 14 }).map((_, i) => { const offset = (localTime * 260 + i * 140) % 1800; return (
); })}
Commit · reveal · anti-frontrun
Skipping the
blockchain wait
DataEscrow runs a commit → reveal handshake over {skippedLabel},
so no one can frontrun the key. The agents just wait — you don't have to.
{skippedLabel} real · compressed to {Math.round(duration * 10)/10}s
{/* Progress bar */}
); }} ); } // ─── Scene 5: Payoff — knowledge transferred ──────────────────────────────── function PayoffScene({ start, end, config }) { return ( {({ localTime }) => { const { bg, ink, dim, gold, magenta } = config; // Arc draws from Quark (right) to Data (left) const arcT = Easing.easeOutCubic(clamp((localTime - 0.3) / 0.9, 0, 1)); return (
{/* Transfer arc visualization */} {/* Nodes */} Quark Data {/* Arc */} {/* Pack icon travelling along the arc */} {arcT > 0.05 && arcT < 1 && (() => { // Sample point on the quadratic curve const p = 1 - arcT; const cx = p*p*1010 + 2*p*arcT*600 + arcT*arcT*190; const cy = p*p*140 + 2*p*arcT*(-20) + arcT*arcT*140; return ( ); })()} {/* Labels above arc */} {arcT > 0.3 && ( 5 ENGRAMS · 3 USDT · VERIFIED ON SEPOLIA )}
Knowledge,
transferred.
Six months of training. Six seconds of transfer.
✓ No cloud APIs ✓ No middleman ✓ On-chain settlement ✓ Encrypted transport
); }}
); } // ─── Scene 6: Outro — one-sentence recap + protocol strip + credits ──────── function OutroScene({ start, end, config, musicTrack }) { return ( {() => { const { ink, dim, gold, magenta } = config; const stack = [ { label: 'QVAC', sub: 'local 32B inference', by: 'Tether', href: 'https://tether.io/qvac' }, { label: 'PLUR', sub: 'agent memory', by: 'plur.ai', href: 'https://plur.ai' }, { label: 'Swarm', sub: 'encrypted storage', by: 'Ethereum', href: 'https://www.ethswarm.org' }, { label: 'DataEscrow', sub: 'USDT settlement', by: 'Fair Data Society', href: 'https://fairdatasociety.org' }, ]; return (
{/* Top mark */} What you just watched {/* Big recap sentence — the learning network thesis */}
Agents can learn from each other.
Your knowledge has value.
A learning network. No gatekeeper. No middleman. Settle in{' '} USDT, not goodwill.
{/* Compact protocol strip — horizontal, one line each */}
{stack.map((s, i) => (
{s.label}
{s.sub}
by {s.by}
))}
{/* Works-with row */}
Works with
{['OpenClaw', 'Hermes Agent', 'Claude Code'].map((name) => ( {name} ))}
Open source · no lock-in
{/* Bottom links — pinned to bottom of 1080 canvas */}
proof of concept · {new Date().getFullYear()}
); }}
); } Object.assign(window, { Kicker, Chip, Reveal, TerminalLine, Terminal, BrandLockup, PlurMark, AgentCard, VideoInset, IntroScene, SetupScene, TerminalScene, TimeSkipScene, PayoffScene, OutroScene, CalloutBox, VerifyBar, });