Motion · Outro

Triple neon marquee recap

Three rows of opposing neon marquee text (BETTER/FASTER/STRONGER) scroll endlessly, taking turns lighting up, then fade out.

1920×108016:930 fps5.06s · 152 framesremotionLicense: Apache-2.0#shotcraft#remotion#outro#typography

Source: video-shotcraft (Vincent Wei) ↗ · Apache-2.0 · Remotion composition (React/TSX) — preview video, no in-browser player

When to use

Recap section for the outro theme words; the "afterglow" beat for a three-word slogan (a step down after cel-flash-stomp's slam); music sections with no voiceover

Intention

One row of marquee is just web decor. Stack three rows, alternate direction on odd/even rows, and cycle the brightness by phase, and it becomes "the theme words parading past the viewer." The key is alternating light-up: at any moment only one row is the neon lead, the rest dim to thin outline backdrops — all three lit at once is a broken sign, taking turns is a recap. The opposing scroll directions (top/bottom rows right, middle row left) create an "enclosed" feel: the eye is pinned in the middle as words stream past from both sides, reading as "these three words never stop."

Duration & energy

  • Duration: 4–5s (demo 150f: 10f fade-in + loop body + 20f fade-out)
  • Energy: Medium-high (continuous flow + row-by-row pulsing, no instantaneous impact)

Shot recipe (original Chinese — timing, easing, parameter table, known pitfalls)

意图

一行 marquee 只是网页装饰;三行叠起来、奇偶反向、再让亮度按相位轮流 走,就成了"主题词在观众眼前列队巡游"。命门是明暗轮唱:任一时刻 只有一行是霓虹主角,其余压暗成细描边背景——三行全亮是灯牌事故, 轮流亮才是 recap。反向对滚制造"包围感":上下行向右、中行向左, 视线被夹在中间,词从两侧源源不断流过,读作"这三个词说不完"。

动效核心

  • 无缝回绕:副本按固定 unitW 等距绝对定位,(frame*speed) % unitW

取模平移——匀速 linear(marquee 类是库内 linear 豁免项)

  • 空心描边字:-webkit-text-stroke,color transparent;亮度脉冲

同时驱动三件事——描边宽 5→8px、opacity 0.35→1、双层 drop-shadow 辉光(8→30px + 20→70px)——只调 opacity 不调辉光读不出"霓虹通电"

  • 轮唱相位:周期 45f,行间相位差 15f(周期/3),余弦软包络且每行

只占周期前 1/3 亮、后 2/3 归零——保证"一亮俩暗"永远成立

  • 三行配色蓝/粉/琥珀(#4d9fff/#ff4dd2/#ffb347),深底 #050308;

中行反向且略快(14/17/14 px/f)——同速对滚读作镜像,破掉

  • 词尾加分隔点(•)撑词间距,unitW = 估宽 + 1.3 倍字号空隙

参数表

| 参数 | 典型值 | 调节手感 | |------|--------|----------| | 字号/行距 | 300px,行距 350px 排满 1080 | 行间留白 >80px 读作三条横幅广告而非一面词墙 | | 滚速 | 14/17/14 px/f,中行反向 | >25 读不清词只见光带;三行同速同向读作整版平移 | | 脉冲周期 | 45f,相位差 15f | 周期 <30f 三行抢闪读作故障;>70f 轮唱感断掉 | | 亮度区间 | opacity 0.35→1 + stroke 5→8px | 暗态 <0.25 暗行消失,"三行"结构丢了 | | 辉光 | 双层 drop-shadow,亮态 30/70px | 单层或 <20px 读作普通描边字不读作霓虹 | | 淡入淡出 | 入 10f / 出 20f 整组 opacity | 循环体无始无终,掐头去尾全靠这两端;硬切进出读作素材没铺满 |

已知坑

  • demo 在灰阶/占位素材上调校通过——参数是调校起点非实战定稿,

首次实战须以真实素材回验

  • 与 page-waterfall-wall 分工:那卡是页面截图纵向瀑布做"内容体量感",

本卡是文字行横向对滚做"主题词余韵"——素材是词不是页,位置在片尾不在中段

  • 与 outro-group-photo-launch 分工:全家福是峰值终镜(元素合影+发布会收场),

本卡是终镜前的 recap 铺垫或低成本备选终镜,两者可串(本卡→淡出→全家福)不可叠

  • 与 cel-flash-stomp 同吃"三连词口号"文案:那卡逐词砸(高能宣告),

本卡整组滚(余韵复读)——同一组词全片最多各用一次,先砸后滚顺序不可反

  • 副本数按 ceil(1920/unitW)+3 留冗余——短词(≤4 字符)unitW 小,

副本不足会在回绕瞬间露右侧空缺

  • 实战品牌色版:三行可统一品牌色靠明暗区分,但辉光对比须 ≥ demo 的

0.35→1 幅度,不然轮唱读不出来

参考实现

demos/outro/neon-triple-marquee/ (NeonTripleMarquee.tsx) 原片出处:clickup-30 61–64.5s

Reference implementation (Remotion)

  • demos/outro/neon-triple-marquee/NeonTripleMarquee.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/outro/neon-triple-marquee/NeonTripleMarquee.tsx

The component imports shared fixtures from demos/_fixtures and helpers from assets/lib in the repo; SKILL.md there documents the render workflow.

Everything on this page is also machine-readable. Point your agent at the JSON, or add the gimgs MCP server and ask it to search motion templates.

  1. Fetch the JSON — it holds the shot recipe (timing, easing, parameter table, pitfalls) and the full TSX source.
  2. Clone the video-shotcraft repo; the component imports shared fixtures from demos/_fixtures and helpers from assets/lib.
  3. Register the component as a <Composition> in a Remotion project and render with `npx remotion render`, or re-implement the recipe in your own engine.
curl -s https://gimgs.net/motion/neon-triple-marquee.json
git clone https://github.com/Vincentwei1021/video-shotcraft
# component: demos/outro/neon-triple-marquee/NeonTripleMarquee.tsx
npx remotion render <CompositionId> out.mp4
claude mcp add --transport http gimgs https://gimgs.net/mcp
# then: search_motion_templates / get_motion_template("neon-triple-marquee")

Prompt

Use the video-shotcraft shot recipe "Triple neon marquee recap" (Remotion composition, Apache-2.0) published on gimgs.net.
Recipe JSON (prompt = timing/easing/parameter table/pitfalls, files = TSX source): https://gimgs.net/motion/neon-triple-marquee.json
Raw TSX: https://gimgs.net/motion/neon-triple-marquee/template.html
Repository: https://github.com/Vincentwei1021/video-shotcraft  (component: demos/outro/neon-triple-marquee/NeonTripleMarquee.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).

JSON · Markdown · llms.txt

3,384 chars · Apache-2.0Open raw template ↗
// ===== demos/outro/neon-triple-marquee/NeonTripleMarquee.tsx =====
// neon-triple-marquee —— clickup-30 61–64.5s
// 三行霓虹描边巨字 BETTER/FASTER/STRONGER 满屏排布,
// 奇偶行反向匀速无限滚动(marquee 允许 linear),
// 三行明暗轮流脉冲(一行亮时其余压暗),结尾整组淡出。
import React from 'react';
import { AbsoluteFill, useCurrentFrame, interpolate } from 'remotion';

const FONT = '"Arial Black", "Helvetica Neue", Arial, sans-serif';

// 单行 marquee:副本按固定 unitW 等距绝对定位,平移取模 → 回绕无缝。
const MarqueeRow: React.FC<{
  word: string;
  color: string;
  dir: 1 | -1;
  speed: number; // px/frame
  frame: number;
  y: number;
  fontSize: number;
  brightness: number; // 0..1 脉冲亮度
}> = ({ word, color, dir, speed, frame, y, fontSize, brightness }) => {
  // 逐字符估宽(上限估计),保证槽位 >= 实际文本宽,副本不重叠
  const est = word.length * fontSize * 0.92;
  const unitW = est + fontSize * 1.3; // 词间空隙(含分隔点)
  const copies = Math.ceil(1920 / unitW) + 3;
  const offsetRaw = (frame * speed) % unitW;
  const offset = dir === 1 ? -unitW * 1.5 + offsetRaw : -unitW * 0.5 - offsetRaw;

  const strokeW = 5 + brightness * 3;

  return (
    <div
      style={{
        position: 'absolute',
        top: y,
        left: 0,
        width: '100%',
        height: fontSize * 1.1,
        overflow: 'visible',
        transform: `translateX(${offset}px)`,
        fontFamily: FONT,
        fontWeight: 900,
        fontSize,
        letterSpacing: 4,
        lineHeight: 1,
        color: 'transparent',
        WebkitTextStroke: `${strokeW}px ${color}`,
        opacity: 0.35 + brightness * 0.65,
        filter: `drop-shadow(0 0 ${8 + brightness * 22}px ${color}) drop-shadow(0 0 ${
          20 + brightness * 50
        }px ${color})`,
      }}
    >
      {Array.from({ length: copies }).map((_, i) => (
        <span
          key={i}
          style={{ position: 'absolute', left: i * unitW, top: 0, whiteSpace: 'nowrap' }}
        >
          {word}
          <span style={{ display: 'inline-block', transform: `translateX(${fontSize * 0.4}px)` }}>
            {'•'}
          </span>
        </span>
      ))}
    </div>
  );
};

export const NeonTripleMarquee: React.FC = () => {
  const f = useCurrentFrame();

  // 三行轮流脉冲:周期 45 帧,每行占 1/3 相位,余弦软脉冲
  const pulse = (idx: number) => {
    const period = 45;
    const phase = (((f - idx * (period / 3)) % period) + period) % period;
    const t = phase / period;
    if (t < 1 / 3) return 0.5 - 0.5 * Math.cos(t * 3 * Math.PI * 2);
    return 0;
  };

  // 入场淡入 + 结尾整组淡出
  const groupOpacity = interpolate(f, [0, 10, 128, 148], [0, 1, 1, 0], {
    extrapolateLeft: 'clamp',
    extrapolateRight: 'clamp',
  });

  const rows: { word: string; color: string; dir: 1 | -1; speed: number }[] = [
    { word: 'BETTER', color: '#4d9fff', dir: 1, speed: 14 },
    { word: 'FASTER', color: '#ff4dd2', dir: -1, speed: 17 },
    { word: 'STRONGER', color: '#ffb347', dir: 1, speed: 14 },
  ];

  return (
    <AbsoluteFill style={{ background: '#050308', overflow: 'hidden' }}>
      <div style={{ opacity: groupOpacity, position: 'absolute', inset: 0 }}>
        {rows.map((r, i) => (
          <MarqueeRow
            key={r.word}
            word={r.word}
            color={r.color}
            dir={r.dir}
            speed={r.speed}
            frame={f}
            y={40 + i * 350}
            fontSize={300}
            brightness={pulse(i)}
          />
        ))}
      </div>
    </AbsoluteFill>
  );
};


  1. Read the recipe
    Prompt = when to use, intention, parameter table with typical values and how each one feels, known pitfalls. Treat values as calibrated starting points.
  2. Get the code
    tool: git
    git clone https://github.com/Vincentwei1021/video-shotcraft && cd video-shotcraft && npm install
  3. Register + render
    tool: remotion
    npx remotion render <CompositionId> out.mp4 # component: demos/outro/neon-triple-marquee/NeonTripleMarquee.tsx
  4. Or re-implement
    Port the timing/easing from the recipe to HTML + WAAPI (gimgs motion contract) or any engine; keep the same segment windows and easing.
paramtypedefaultdescription
—

More in this category