К основному содержанию
скилл CX Codex

countdown-video — скилл для Claude Code: таймер видео без дрейфа

Скилл для сборки видео с обратным отсчётом: таймер, цифровые перекаты, формат mm:ss или dd:hh:mm:ss. Пригодится для экранов «скоро начнём» и отсчётов до запуска.

Установить в Claude Code · Через Skills CLI (Vercel) Все способы и платформы ↓
npx skills add iart-ai/tiktok-video-skills --skill countdown-video -a claude-code
Открыть на GitHub

Что делает countdown-video

Скилл задаёт один принцип: оставшееся время считается от кадра или монотонной метки времени, а не уменьшается через setInterval. В Remotion это useCurrentFrame() и формула total − frame/fps, на живой странице — performance.now() или Date.now(). Пересчёт идёт каждый кадр, поэтому поздний кадр сам себя исправляет, а ошибка не накапливается.

Длительность здесь — входной параметр durationInSeconds, а не константа: одна композиция отрисует и пятиминутный экран стрима, и короткий отсчёт распродажи. Формат выбирается под интервал — mm:ss, hh:mm:ss или dd:hh:mm:ss, лишние ведущие нули отбрасываются. Цифры ставятся на tabular-nums, иначе вёрстка дёргается при смене чисел. Перекаты цифр сделаны как вертикальная лента 0–9 со сдвигом по позиции, а не подменой текста.

Внутри есть справочники references/countdown-cookbook.md и references/digit-flip.md, а также скрипты scripts/contact-sheet.sh для склейки кадров и scripts/probe-mp4.sh для проверки энкода. Скилл рассчитан на Remotion: без него схема с useCurrentFrame() не заработает. Результат — out/*.mp4 и сам проект, по умолчанию вертикаль 1080×1920.

Какие задачи решает

  • собрать экран «скоро начнём» для стрима с таймером на 5–10 минут
  • сделать отсчёт до запуска или распродажи в формате dd:hh:mm:ss
  • добиться, чтобы таймер попадал в ноль на нужном кадре
  • добавить перекат цифр вместо резкой смены числа
  • зациклить фоновую анимацию без шва на стыке

Примеры запросов агенту

  • сделай видео с обратным отсчётом на 5 минут для экрана «скоро начнём», вертикаль 1080×1920
  • собери отсчёт до даты запуска в формате dd:hh:mm:ss с перекатом цифр
  • зацикли фоновую анимацию под таймер, чтобы не было видно стыка

Как установить countdown-video

1Через Skills CLI (Vercel)

npx skills add iart-ai/tiktok-video-skills --skill countdown-video -a claude-code

2Вручную: положить файлы

~/.claude/skills/countdown-video/SKILL.md

Папка скилла целиком; для одного проекта — .claude/skills/countdown-video/

По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов

Вопросы о countdown-video

Как установить countdown-video в Claude?

Выполните npx skills add iart-ai/tiktok-video-skills --skill countdown-video -a claude-code. Скилл окажется в ~/.claude/skills/countdown-video/SKILL.md.

Что делает countdown-video?

Собирает видео с обратным отсчётом: считает оставшееся время от кадра, форматирует его и анимирует цифры перекатом. Длительность задаётся параметром durationInSeconds.

Почему таймер нельзя делать через setInterval?

Такой колбэк срабатывает не раньше чем через 1000 мс и накапливает отставание при троттлинге вкладки или медленном кадре. Скилл пересчитывает время от авторитетных часов каждый кадр.

Исходный текст

SKILL.md · MIT
---
name: countdown-video
description: This skill should be used when the user asks to "make a countdown timer video", "create a countdown animation", "build a stream starting soon countdown", "do an event/launch/sale countdown", "animate a mm:ss or dd:hh:mm:ss timer", "add digit roll/flip transitions to a counter", or "loop a countdown background". Covers frame-accurate (drift-free) timers, digit flip/roll motion, time formatting, and a configurable duration prop.
version: 0.1.0
---

# Countdown Video

Make a countdown that ends exactly when it should. The whole craft is one idea: derive the remaining time from the frame (or a monotonic timestamp), then format and animate that number — never count down with a `setInterval`. Covers livestream "starting soon" screens, launch/sale countdowns, and looping backgrounds.

## When to use

- "Starting soon" / "be right back" livestream screens with a 5–10 min timer.
- Launch and flash-sale countdowns (dd:hh:mm:ss down to a target date; mm:ss for urgency).
- Any animated counter where the number must land on zero on the exact frame.

## The one rule: drive the number from the clock, never a tick

A `setInterval(fn, 1000)` countdown **drifts** — the callback fires *at least* 1000 ms later, never exactly, and stacks up when the tab is throttled or a render frame is slow. Reports of ~1 second lost per minute are common. The fix is the same for rendered video and live web: compute remaining time as a pure function of an authoritative clock, so a late frame self-corrects on the next frame instead of accumulating error.

| Context | Authoritative clock | Remaining time |
|---|---|---|
| Remotion render | `useCurrentFrame()` | `total − frame/fps` |
| Web (live page) | `performance.now()` or `Date.now()` | `targetMs − now` |
| To a fixed date | `Date.now()` | `targetTimestamp − Date.now()` |

Never store remaining time in state and decrement it. Recompute it every frame.

## Frame-accurate countdown (Remotion → MP4)

`useCurrentFrame()` is deterministic: frame N is always rendered at exactly `N/fps` seconds. Subtract from the total and the timer is drift-free by construction.

```tsx
import { useCurrentFrame, useVideoConfig } from "remotion";

export const Countdown: React.FC<{ durationInSeconds: number }> = ({
  durationInSeconds,
}) => {
  const frame = useCurrentFrame();
  const { fps } = useVideoConfig();

  // remaining seconds, clamped at 0 — ceil so "1" shows for its full second
  const elapsed = frame / fps;
  const remaining = Math.max(0, Math.ceil(durationInSeconds - elapsed));

  return <div style={{ fontVariantNumeric: "tabular-nums" }}>{formatMMSS(remaining)}</div>;
};
```

`durationInSeconds` is a **prop**, not a constant — the same composition renders a 5-minute stream timer or a 30-second sale timer by changing one input. Set the composition's `durationInFrames` to `durationInSeconds * fps` so the video is exactly as long as the countdown.

## Web timer (live page, requestAnimationFrame)

For a live page, drive an rAF loop from `performance.now()` and only repaint when the displayed second changes (no work 60×/sec for a 1 Hz number):

```js
const start = performance.now();
let lastShown = -1;
function tick(now) {
  const remaining = Math.max(0, Math.ceil(duration - (now - start) / 1000));
  if (remaining !== lastShown) { render(remaining); lastShown = remaining; }
  if (remaining > 0) requestAnimationFrame(tick);
}
requestAnimationFrame(tick);
```

Counting down to a fixed date is the same idea with `Date.now()`: `Math.max(0, target - Date.now())`. See `references/countdown-cookbook.md` for the full date-target version with timezone handling.

## Time formatting

Break a remaining-seconds integer into fields with division + modulo, then zero-pad. Pick the format to fit the duration — never show `00d 00h 05m` when minutes is all that matters.

```js
const pad = (n) => String(n).padStart(2, "0");
const formatMMSS = (s) => `${pad(Math.floor(s / 60))}:${pad(s % 60)}`;
const formatDHMS = (s) => {
  const d = Math.floor(s / 86400), h = Math.floor((s % 86400) / 3600);
  const m = Math.floor((s % 3600) / 60), sec = s % 60;
  return `${pad(d)}:${pad(h)}:${pad(m)}:${pad(sec)}`;
};
```

| Format | Use | Drop fields when |
|---|---|---|
| `mm:ss` | stream "starting soon", short timers (<1h) | — |
| `hh:mm:ss` | flash sale, same-day event | hide `dd` while 0 |
| `dd:hh:mm:ss` | launch / multi-day countdown | hide leading 0 units for cleaner read |

Always set `font-variant-numeric: tabular-nums` (or a monospace digit font) so digits keep equal width and the layout does not jitter as numbers change.

## Digit roll / flip transitions

A flat number swap reads as cheap; the rolling "odometer" look sells the countdown. Render each digit as a vertical strip 0–9 and translate it by `-digit × digitHeight`. Animate the *position*, not the text content, so the change is a glide, not a pop.

```css
.reel { height: 1em; overflow: hidden; }
.reel .col { transition: transform .45s cubic-bezier(.22,1,.36,1); }
/* col holds stacked 0..9; shift by -value em */
```

In Remotion, drive the strip offset off the frame instead of a CSS transition (so it is deterministic). The flip-card style (top half folds down over the bottom) and the per-digit reel are both detailed, with runnable components, in `references/digit-flip.md`.

## Looping background

Stream screens hold for minutes, so the background must loop seamlessly. Build motion on a cycle that divides evenly into the timeline (e.g. a gradient or particle drift whose period is `loopFrames`), and read it as `frame % loopFrames` so there is no visible seam at the wrap. Keep it low-contrast and slow — the number is the subject; the background is ambience. Recipes in `references/countdown-cookbook.md`.

## Output checklist

- Remaining time recomputed from `useCurrentFrame()` / `performance.now()` every frame — no `setInterval` decrement.
- Timer hits `00:00` on the exact intended frame; `durationInFrames === durationInSeconds × fps`.
- Duration is an input prop, not a hardcoded constant.
- Digits use `tabular-nums`; layout does not shift as numbers change.
- Format matches the span (mm:ss vs dd:hh:mm:ss); leading zero units dropped when distracting.
- Background loop period divides the timeline evenly; no seam at the wrap.

## Deliver & verify (rendered stills → MP4)

> **Packaged helper** (`scripts/`): tile your stills with `scripts/contact-sheet.sh sheet.png f-hook.png f-mid.png f-end.png`, then assert the encode with `scripts/probe-mp4.sh out.mp4 [WxH] [fps]`. See `scripts/README.md`.

The countdown ships as a Remotion composition (`<Composition>` + zod `schema` + `defaultProps`, `durationInSeconds` a prop) — the timer is a pure function of `useCurrentFrame()`, never `setInterval` / `Date.now()` / `Math.random()`. Set `durationInFrames = durationInSeconds * fps`. Deliverable = `out/*.mp4` + the project. 9:16 vertical (1080×1920) is the default.

**Verify loop — render stills → inspect → encode.** The whole point is frame-accuracy, so check the *digit value* at exact frames before encoding.

```bash
# Stills at start / mid / end — WITH SHIPPED PROPS (the real duration you'll render)
npx remotion still Countdown out/f-start.png --frame=0   --props='{"durationInSeconds":300}'
npx remotion still Countdown out/f-mid.png   --frame=N   --props='{"durationInSeconds":300}'
npx remotion still Countdown out/f-end.png   --frame=L   --props='{"durationInSeconds":300}'  # L = durationInFrames-1

# Inspect each PNG — the digits must read EXACTLY the frame/fps math:
#  - at frame N, displayed = ceil(durationInSeconds - N/fps)  (e.g. 300s @ 30fps, frame 4500 → 150s → "02:30")
#  - last frame reads 00:00 (or the end-state hold); no drift, no off-by-one second
#  - tabular-nums: layout does not jitter between frames
#  - 9:16 safe area: the timer + brand block sit clear of top ~12% and bottom ~20-35% UI and the right action rail

npx remotion render Countdown out/countdown.mp4 --props='{"durationInSeconds":300}'   # encode once digits verify
npx remotion render Countdown out/demo.gif --codec=gif                                 # README proof clip
```

Use `npx remotion compositions` to confirm `durationInFrames`/`fps`, then compute the frame for any second you want to check.

**Before you finish:**
1. Stills render cleanly at frame 0, mid, and last — no errors.
2. The digit value at frame N equals `ceil(durationInSeconds - N/fps)` exactly; last frame hits 00:00 on the intended frame (no drift / off-by-one).
3. Timer + brand stay inside the 9:16 safe area at every checked frame.
4. Frame-driven only — no `setInterval` / `Date.now()` / timers; `tabular-nums` so layout holds.
5. Shipped `durationInSeconds` (not just `defaultProps`) renders correctly; MP4 encoded, GIF optional.

## Reference files

- `references/countdown-cookbook.md` — complete runnable Remotion `<Countdown>` (configurable duration, mm:ss / dd:hh:mm:ss, end-state hold), the count-down-to-a-fixed-date web component with timezone handling, a "starting soon" stream-screen layout (brand block + social handles + looping gradient background), and the seamless-loop background pattern.
- `references/digit-flip.md` — two deterministic digit-animation components: the odometer reel (vertical 0–9 strip driven by frame) and the flip-card (top-half fold) effect, plus when each suits launch vs sale vs stream.

© iart-ai · MIT · iart-ai/tiktok-video-skills

Похожие скиллы

Всё по теме «Фронтенд и UI» →
remotion-video-toolkit shreefentsar Скилл учит ИИ-агента писать код Remotion: анимации, тайминг, рендер в MP4, субтитры, графики и 3D. Пригодится, когда видео генерируется из данных, а не собирается руками в редакторе. CX Codex +1 779установок remotion-video-creation affaan-m Скилл подсказывает агенту правила работы с Remotion в React: от анимаций и звука до субтитров, графиков и переходов. Пригодится при сборке видео из React-компонентов. CX Codex +1 4 900установок remotion-video iart-ai Скилл для сборки MP4, GIF и WebM прямо в React: каждый кадр — чистая функция от useCurrentFrame, поэтому рендер детерминированный и повторяемый. CX Codex +1 2 600установок wrapped-video iart-ai Скилл собирает вертикальные персональные видео из строки данных: один шаблон, много роликов. Годится для итогов года по каждому пользователю. CX Codex +1 1 300установок yyl-remotion-video ttfake92-lab Превращает статью, сценарий или заметки в проект Remotion и рендерит mp4. Пригодится, когда нужен видеофайл, а не веб-презентация. CX Codex +1 503установок remotion-motion-graphics haidrrrry Скилл для ИИ-кодинга, который заставляет агента писать код Remotion по правилам моушн-дизайна, а не выдавать линейные фейды и плоские фоны. CX Codex +1 1 100установок

Из того же набора4

Спонсоры проекта

Компании, которые помогают держать аналитику открытой