Motion · Interação
AI Response Stream-In
The AI response panel drops a readable summary first, then status-icon evidence rows stream in one by one, settling into a completed state.
Fonte: video-shotcraft (Vincent Wei) ↗ · Apache-2.0 · Composição Remotion (React/TSX) — vídeo de prévia, sem player no navegador
When to use
Result-generation shot for AI assistants/agents/search/copilot; emphasizes "conclusion first, evidence follows, task complete."
Intention
Shoot "the AI is working" as a readable causal chain, not a log-scroll flood: viewers first read the answer summary, then watch evidence/sub-tasks fill in one by one, and finally the completed state confirms this round of work is done.
Duration & energy
- Duration: About 4–5s (120–150f, including ≥15f hold on the completed state).
- Energy: Medium-high (information keeps accumulating, but readability outranks speed showmanship).
Shot recipe (original Chinese — timing, easing, parameter table, known pitfalls)
意图
把“AI 正在工作”拍成可读的因果链,而不是日志刷屏:观众先看懂答案摘要, 再看到证据/子任务逐条补齐,最后由完成态确认这轮工作结束。
动效核心
- 用目标产品真实响应面板截图做 backplate;动态区截图前隐藏,按 layout 坐标重叠回真实槽位(Q1/Q9)
- 面板进入后摘要句先以短 wipe/淡入落定,落定后至少 12f 才起首条证据
- 6–8 条证据行自下方 14–24px + 轻微 blur 逐行汇入;间隔逐渐收紧但保留可数节拍
- 行体先停,状态图标晚 2–4f 由 pending 转 running/done,形成主次拖拽层级
(模式已沉为 assets/lib/helpers/motion.ts lagged)
- 末条完成后只做一次面板级完成脉冲,随后全画面静止 ≥15f;不逐行 glint
参数表
| 参数 | 典型值 | 调节手感 | |------|--------|----------| | 摘要 | cue 18,10–14f reveal;摘要落定后 hold ≥12f,首行 cue ≥42 | 摘要与首行粘连会失去“结论先到”的叙事;正常速度必须能先读到一句完整结论 | | 行节拍 | cue[i]=42+[0,11,21,30,38,45,51,56][i],每行 10–14f | 间隔 11→5f 逐渐收紧,表达工作加速;不要密到所有行同时糊成一团(R2) | | 行入场 | y 18px→0,blur 6px→0,opacity 0→1,bezier(0.2,0.75,0.25,1) | 位移须过正常速度肉眼阈值;大幅横飞会像通知列表,不像证据归档 | | 状态图标 | 比行体 cue 晚 3f;pending 环 → running 缺口环 → done 实心勾,6–8f | 图标是状态回执,不抢行文本;同帧切换全部图标会读作假 loading | | 完成收束 | 末图标 done 后 6f,面板边框/摘要底色 opacity 0.25→0.55→0.25 共 10f | 只做面板级一次,不逐行发光(Q4);完成脉冲后留 ≥15f 真静止(R1) | | 相机 | 信息密集默认正视,zoom 1.04→1.0 微退;暗场斜拍仅作情绪变体 | Linear 参考片的斜拍不是通用命令;文字读不清时立即回正视(Q6) |
声音
摘要落定配一发 transition-soft;前 3–4 行用短 pop 轻声递减 0.32→0.20,后半加速密集段合并成一条低音量 whoosh-fast,避免逐行机枪感; 最终完成态用 click-camera 的轻量版本(约 vol 0.3)。声音在画面锁定后钉帧。
已知坑
- 逐字符流式打字会把视线困在字符层;宣传片时长里优先按“语义块”揭示摘要,
真正展示输入操作时另用 type-and-filter
- 行体和状态图标若同帧落定,会像静态列表整体切换;保留 2–4f 拖拽层级
- 行终点必须是截图里真实列表槽位,不在面板上方悬浮;动态内容也必须用虚构数据
- 此卡借鉴 Linear Agent 发布片 t≈13–17s(进化轮 #3 breakdown #6),非用户判例;
参数未经本库实战项目验证,首次使用必须按 P1 在正常速度下看片,不以逐帧 diff 代替体感
参考实现
demos/interaction/ai-stream-response/ (StreamResponse.tsx)
Reference implementation (Remotion)
- demos/interaction/ai-stream-response/StreamResponse.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/interaction/ai-stream-response/StreamResponse.tsx
The component imports shared fixtures from demos/_fixtures and helpers from assets/lib in the repo; SKILL.md there documents the render workflow.
Tudo nesta página também é legível por máquina. Aponte seu agente para o JSON ou adicione o servidor MCP do gimgs e peça para buscar templates de motion.
- Baixe o JSON — contém a receita do plano (timing, easing, tabela de parâmetros, armadilhas) e o código TSX completo.
- Clone o repositório video-shotcraft; o componente importa fixtures de demos/_fixtures e helpers de assets/lib.
- Registre o componente como <Composition> em um projeto Remotion e renderize com `npx remotion render`, ou reimplemente a receita no seu próprio engine.
curl -s https://gimgs.net/motion/ai-stream-response.jsongit clone https://github.com/Vincentwei1021/video-shotcraft
# component: demos/interaction/ai-stream-response/StreamResponse.tsx
npx remotion render <CompositionId> out.mp4claude mcp add --transport http gimgs https://gimgs.net/mcp
# then: search_motion_templates / get_motion_template("ai-stream-response")Prompt
Use the video-shotcraft shot recipe "AI Response Stream-In" (Remotion composition, Apache-2.0) published on gimgs.net. Recipe JSON (prompt = timing/easing/parameter table/pitfalls, files = TSX source): https://gimgs.net/motion/ai-stream-response.json Raw TSX: https://gimgs.net/motion/ai-stream-response/template.html Repository: https://github.com/Vincentwei1021/video-shotcraft (component: demos/interaction/ai-stream-response/StreamResponse.tsx; imports demos/_fixtures and assets/lib) Spec: 1920x1080, 30 fps, 5.06s 1. Read the JSON and follow its "prompt" section (when to use, intention, parameter table, known pitfalls). 2. Adapt it for: <describe what you want>. 3. Either clone the repo, register the component as a <Composition> in a Remotion project and run npx remotion render <CompositionId> out.mp4 — or re-implement the recipe in your own engine (HTML/WAAPI works: keep the same timing and easing).
// ===== demos/interaction/ai-stream-response/StreamResponse.tsx =====
import React from 'react';
import {
AbsoluteFill,
Easing,
Img,
interpolate,
useCurrentFrame,
} from 'remotion';
import backplate from './agent-stream.jpg';
const ROW_CUES = [42, 53, 63, 72, 80, 87, 93];
const ROWS = [
['Indexed the workspace', '128 files'],
['Mapped the authentication flow', '12 modules'],
['Checked recent error traces', 'No blockers'],
['Matched API contracts to handlers', '24 routes'],
['Verified permission boundaries', '6 roles'],
['Cross-checked release notes', '3 changes'],
['Prepared an implementation plan', 'Ready'],
] as const;
const clamp = {extrapolateLeft: 'clamp', extrapolateRight: 'clamp'} as const;
const ease = Easing.bezier(0.2, 0.75, 0.25, 1);
const CheckIcon: React.FC<{progress: number}> = ({progress}) => {
const ringOpacity = interpolate(progress, [0, 0.45, 0.75], [0.36, 1, 0], clamp);
const checkOpacity = interpolate(progress, [0.62, 1], [0, 1], clamp);
const checkScale = interpolate(progress, [0.62, 1], [0.72, 1], {...clamp, easing: ease});
return (
<div style={{position: 'relative', width: 27, height: 27, flex: '0 0 auto'}}>
<div
style={{
position: 'absolute', inset: 2, borderRadius: 99,
border: '2px solid rgba(183, 190, 199, .6)',
borderTopColor: '#d4ff77', opacity: ringOpacity,
transform: `rotate(${progress * 100}deg)`,
}}
/>
<div
style={{
position: 'absolute', inset: 1, borderRadius: 99,
background: '#b8f36a', opacity: checkOpacity,
transform: `scale(${checkScale})`,
display: 'grid', placeItems: 'center',
boxShadow: '0 0 16px rgba(184,243,106,.18)',
}}
>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
<path d="M3.4 8.3 6.6 11l6-6" stroke="#15200d" strokeWidth="2.1" strokeLinecap="round" strokeLinejoin="round"/>
</svg>
</div>
</div>
);
};
const EvidenceRow: React.FC<{cue: number; title: string; meta: string; index: number}> = ({cue, title, meta, index}) => {
const frame = useCurrentFrame();
const body = interpolate(frame, [cue, cue + 12], [0, 1], {...clamp, easing: ease});
const status = interpolate(frame, [cue + 3, cue + 11], [0, 1], {...clamp, easing: ease});
return (
<div
style={{
position: 'absolute', left: 0, right: 0, top: index * 68,
height: 58, borderRadius: 13,
border: '1px solid rgba(255,255,255,.065)',
background: 'rgba(255,255,255,.025)',
display: 'flex', alignItems: 'center', padding: '0 20px', gap: 15,
opacity: body,
transform: `translateY(${18 * (1 - body)}px)`,
filter: `blur(${6 * (1 - body)}px)`,
}}
>
<CheckIcon progress={status}/>
<div style={{fontSize: 21, color: '#e8eaed', letterSpacing: '-0.015em', flex: 1}}>{title}</div>
<div style={{fontSize: 16, color: status > 0.78 ? '#a9c77f' : '#777f89', fontVariantNumeric: 'tabular-nums'}}>{meta}</div>
</div>
);
};
export const StreamResponse: React.FC = () => {
const frame = useCurrentFrame();
const panelIn = interpolate(frame, [0, 16], [0, 1], {...clamp, easing: ease});
const summary = interpolate(frame, [18, 30], [0, 1], {...clamp, easing: ease});
const camera = interpolate(frame, [0, 100], [1.04, 1], {...clamp, easing: ease});
const pulse = interpolate(frame, [110, 115, 120], [0.25, 0.55, 0.25], clamp);
const complete = interpolate(frame, [110, 120], [0, 1], {...clamp, easing: ease});
return (
<AbsoluteFill style={{background: '#07090a', fontFamily: 'Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif', overflow: 'hidden'}}>
<AbsoluteFill style={{transform: `scale(${camera})`}}>
<Img src={backplate} style={{width: '100%', height: '100%', objectFit: 'cover', filter: 'brightness(.58) saturate(.76) blur(1px)', opacity: .7}}/>
<AbsoluteFill style={{background: 'radial-gradient(circle at 50% 44%, rgba(6,9,10,.08) 10%, rgba(4,6,7,.72) 92%)'}}/>
</AbsoluteFill>
<div
style={{
position: 'absolute', left: 344, top: 72, width: 1232, height: 936,
borderRadius: 29, overflow: 'hidden',
background: 'linear-gradient(145deg, rgba(25,29,32,.985), rgba(12,15,17,.99))',
border: `1px solid rgba(184,243,106,${pulse})`,
boxShadow: '0 46px 100px rgba(0,0,0,.58), inset 0 1px 0 rgba(255,255,255,.06)',
opacity: panelIn,
transform: `translateY(${12 * (1 - panelIn)}px) scale(${.985 + .015 * panelIn})`,
}}
>
<div style={{height: 76, display: 'flex', alignItems: 'center', padding: '0 34px', borderBottom: '1px solid rgba(255,255,255,.065)'}}>
<div style={{width: 29, height: 29, borderRadius: 9, background: '#e9edf0', color: '#15191b', display: 'grid', placeItems: 'center', fontWeight: 800, fontSize: 17}}>A</div>
<div style={{marginLeft: 13, fontSize: 22, fontWeight: 590, color: '#f1f3f4'}}>Ask Atlas</div>
<div style={{marginLeft: 'auto', display: 'flex', alignItems: 'center', gap: 9, color: '#7d858e', fontSize: 15}}>
<span style={{width: 7, height: 7, background: '#b8f36a', borderRadius: 99}}/> Workspace agent
</div>
</div>
<div style={{padding: '28px 38px 34px'}}>
<div style={{height: 48, borderRadius: 12, background: 'rgba(255,255,255,.048)', display: 'flex', alignItems: 'center', padding: '0 18px', color: '#b9bec4', fontSize: 18}}>
Review this codebase and identify the safest implementation path
</div>
<div style={{height: 119, marginTop: 21, borderBottom: '1px solid rgba(255,255,255,.07)', overflow: 'hidden', position: 'relative'}}>
<div style={{position: 'absolute', inset: 0, opacity: summary, transform: `translateY(${10 * (1 - summary)}px)`, clipPath: `inset(0 ${100 * (1 - summary)}% 0 0)`}}>
<div style={{color: '#838b94', fontSize: 15, textTransform: 'uppercase', letterSpacing: '.12em', marginBottom: 10}}>Result summary</div>
<div style={{color: '#f3f4f5', fontSize: 26, fontWeight: 510, lineHeight: 1.35, letterSpacing: '-.022em'}}>
The codebase is ready for a focused, low-risk implementation.
</div>
</div>
</div>
<div style={{position: 'relative', height: 466, marginTop: 17}}>
{ROWS.map(([title, meta], index) => <EvidenceRow key={title} cue={ROW_CUES[index]} title={title} meta={meta} index={index}/>)}
</div>
<div style={{height: 60, marginTop: 2, borderTop: '1px solid rgba(255,255,255,.07)', display: 'flex', alignItems: 'flex-end'}}>
<div style={{display: 'flex', alignItems: 'center', gap: 11, opacity: complete, transform: `translateY(${5 * (1 - complete)}px)`}}>
<div style={{width: 24, height: 24, borderRadius: 99, background: '#b8f36a', display: 'grid', placeItems: 'center'}}>
<svg width="14" height="14" viewBox="0 0 16 16" fill="none"><path d="M3.4 8.3 6.6 11l6-6" stroke="#15200d" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"/></svg>
</div>
<span style={{color: '#dce7d0', fontSize: 17, fontWeight: 560}}>Analysis complete</span>
<span style={{color: '#69717a', fontSize: 15}}>7 checks finished · ready to build</span>
</div>
</div>
</div>
</div>
</AbsoluteFill>
);
};
- Read the recipePrompt = when to use, intention, parameter table with typical values and how each one feels, known pitfalls. Treat values as calibrated starting points.
- Get the codetool: git
git clone https://github.com/Vincentwei1021/video-shotcraft && cd video-shotcraft && npm install - Register + rendertool: remotion
npx remotion render <CompositionId> out.mp4 # component: demos/interaction/ai-stream-response/StreamResponse.tsx - Or re-implementPort the timing/easing from the recipe to HTML + WAAPI (gimgs motion contract) or any engine; keep the same segment windows and easing.
| param | type | default | description |
|---|---|---|---|
| — | |||
Mais nesta categoria

Live voice waveform
A live waveform in a recording pill — 64 thin bars rise and fall with "speech," spiking tall mid-speech and shrinking to a dotted line on pauses, scrolling right to left; a full speak-pause-speak-submit performance.

Type, filter, drill in
Typing a search on a real UI, the grid converges to a single card on its own, then a click drills through into the detail page.

Theme switch: sweep and ripple
Two theme-switch moves — theme-sweep, a diagonal wipe (the skin changes wherever the boundary passes), and palette-ripple, a combo move (a ⌘K panel collapses to a point, a ripple spreads from it to re-skin).
Segmented thumb hero
A segmented control's thumb shift becomes the star of a close-up — a cursor clicks it and a new icon springs in.