countdown-video について
countdown-videoは、Remotion動画やライブWebページ向けの正確なカウントダウン制作スキルです。フレームまたは時計を基準に残り時間を計算してずれを防ぎ、数字の演出、背景ループ、出力確認も扱います。
主な機能
- 01Remotionのフレーム駆動カウントダウン
- 02時計基準のWebタイマー
- 03時間表示形式の設定
- 04オドメーターとフリップ演出
- 05継ぎ目のない背景ループ
- 06レンダリングとMP4の検証
活用シーン
- 01配信の開始前画面
- 02ローンチやフラッシュセールのタイマー
- 03指定日時までのカウントダウン
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ iart-ai/ tiktok-video-skills --skill countdown-videoインストール後、Claude Code で/countdown-videoを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
README.mdreferences/countdown-cookbook.mddigit-flip.md詳細
カウントダウン動画
終了すべき瞬間に、正確に終了するカウントダウンを作成します。基本はひとつです。フレーム(または単調増加タイムスタンプ)から残り時間を求め、その数値をフォーマットしてアニメーションさせます。setInterval でカウントダウンしてはいけません。ライブ配信の「まもなく開始」画面、ローンチやセールのカウントダウン、ループする背景に活用できます。
使用する場面
- 5~10 分のタイマーを表示する「まもなく開始」/「すぐに戻ります」のライブ配信画面。
- ローンチやフラッシュセールのカウントダウン(目標日時までの dd:hh:mm:ss、緊急感を演出する mm:ss)。
- 数字が正確にゼロになるフレームで、アニメーションする必要があるカウンター。
唯一のルール:目盛りではなく、時計を基準に数字を動かす
setInterval(fn, 1000) によるカウントダウンはずれていきます。コールバックは 1000 ms より後に実行され、正確に 1000 ms 後に実行されることはありません。また、タブの処理が間引かれたり、レンダリングフレームが遅れたりすると、ずれが積み重なります。1 分あたり約 1 秒の遅れが発生したという報告もよくあります。レンダリング動画でもライブ Web ページでも、解決方法は同じです。信頼できる時計から残り時間を純粋関数として計算すれば、遅れたフレームは次のフレームで補正され、誤差が積み重なりません。
| コンテキスト | 信頼できる時計 | 残り時間 |
|---|---|---|
| Remotion のレンダリング | useCurrentFrame() | total − frame/fps |
| Web(ライブページ) | performance.now() または Date.now() | targetMs − now |
| 固定日時まで | Date.now() | targetTimestamp − Date.now() |
残り時間を状態に保存して減算してはいけません。フレームごとに再計算します。
フレーム単位で正確なカウントダウン(Remotion → MP4)
useCurrentFrame() は決定論的です。N フレームは常に、ちょうど N/fps 秒の時点としてレンダリングされます。合計時間から差し引くため、タイマーは構造上ずれることがありません。
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 は定数ではなくプロパティです。入力をひとつ変えるだけで、同じコンポジションを 5 分の配信タイマーにも 30 秒のセールタイマーにもレンダリングできます。動画の長さがカウントダウンと正確に一致するよう、コンポジションの durationInFrames を durationInSeconds * fps に設定します。
Web タイマー(ライブページ、requestAnimationFrame)
ライブページでは、performance.now() を基準に rAF ループを動かし、表示する秒数が変わったときだけ再描画します(1 Hz の数値のために毎秒 60 回処理する必要はありません)。
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);
固定日時までのカウントダウンも同じ考え方で、Date.now() を使います。Math.max(0, target - Date.now()) のように計算します。タイムゾーン処理を含む、目標日時を指定する完全なバージョンについては references/countdown-cookbook.md を参照してください。
時刻のフォーマット
残り秒数の整数を、除算と剰余で各フィールドに分けてから、ゼロ埋めします。時間に合ったフォーマットを選びます。分だけで十分な場合に 00d 00h 05m と表示してはいけません。
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)}`;
};
| フォーマット | 用途 | 省略するフィールド |
|---|---|---|
mm:ss | ライブ配信の「まもなく開始」、短いタイマー(1 時間未満) | — |
hh:mm:ss | フラッシュセール、当日開催のイベント | dd が 0 のときは非表示 |
dd:hh:mm:ss | ローンチ/複数日にわたるカウントダウン | 見やすくするため、先頭の 0 の単位を非表示 |
数字の幅を均等に保ち、数値の変化によるレイアウトの揺れを防ぐため、必ず font-variant-numeric: tabular-nums(または等幅数字のフォント)を設定します。
数字のロール/フリップアニメーション
数字をそのまま入れ替えるだけでは安っぽく見えます。回転式オドメーターのような表現で、カウントダウンの魅力を高めましょう。各桁を 0~9 の縦長の帯として描画し、-digit × digitHeight だけ移動させます。アニメーションさせるのはテキストの内容ではなく位置です。これにより、数字が突然切り替わらず、滑らかに動きます。
.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 */
Remotion では、CSS のトランジションではなくフレームを基準に帯のオフセットを動かします(これにより動作が決定論的になります)。上半分が下半分にかぶさるフリップカード形式と、1 桁ずつ回転するリール形式については、実行可能なコンポーネントとともに references/digit-flip.md で詳しく説明しています。
ループする背景
配信画面は数分間表示されるため、背景はシームレスにループする必要があります。タイムラインを割り切れる周期でモーションを構築します(たとえば、周期が loopFrames のグラデーションやパーティクルの移動)。そして frame % loopFrames を使えば、ループの継ぎ目が見えません。コントラストを抑え、動きをゆっくりにします。主役は数字であり、背景は雰囲気を添えるものです。レシピは references/countdown-cookbook.md を参照してください。
出力チェックリスト
- 残り時間は毎フレーム
useCurrentFrame()/performance.now()から再計算します。setIntervalで減算しません。 - タイマーは意図した正確なフレームで
00:00になります。durationInFrames === durationInSeconds × fpsです。 - 再生時間はハードコードされた定数ではなく、入力プロパティにします。
- 数字には
tabular-numsを使用します。数字が変わってもレイアウトはずれません。 - 表示形式は時間の範囲に合わせます(mm:ss と dd:hh:mm:ss)。先頭のゼロ単位は、煩わしい場合は省略します。
- 背景ループの周期はタイムラインを均等に分割し、ループの切り替わりに継ぎ目がありません。
納品と検証(レンダリングした静止画 → MP4)
同梱ヘルパー(
scripts/):scripts/contact-sheet.sh sheet.png f-hook.png f-mid.png f-end.pngで静止画を並べ、scripts/probe-mp4.sh out.mp4 [WxH] [fps]でエンコード結果を検証します。scripts/README.mdを参照してください。
カウントダウンは Remotion コンポジション(<Composition> + zod schema + defaultProps、durationInSeconds はプロパティ)として提供します。タイマーは useCurrentFrame() の純粋な関数であり、setInterval / Date.now() / Math.random() は使用しません。durationInFrames = durationInSeconds * fps を設定します。納品物は out/*.mp4 とプロジェクトです。デフォルトは縦型 9:16(1080×1920)です。
検証ループ — 静止画をレンダリング → 確認 → エンコード。 フレーム精度が最も重要なので、エンコード前に正確なフレームでの数字の値を確認してください。
# 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
npx remotion compositions を使用して durationInFrames / fps を確認し、確認したい秒数のフレームを計算します。
完了前に確認すること:
- フレーム 0、中間、最後の静止画がエラーなくレンダリングされること。
- フレーム N での数字の値が正確に
ceil(durationInSeconds - N/fps)となること。最後のフレームで、意図したフレームに00:00になること(ずれやオフバイワンがないこと)。 - 確認したすべてのフレームで、タイマーとブランドが 9:16 のセーフエリア内に収まること。
- フレーム駆動のみであること —
setInterval/Date.now()/ タイマーを使用しないこと。レイアウトを維持するためにtabular-numsを使用すること。 - 出荷する
durationInSeconds(defaultPropsだけではなく)が正しくレンダリングされること。MP4 をエンコードし、GIF は任意です。
参考ファイル
references/countdown-cookbook.md— 実行可能な Remotion<Countdown>の完全版(再生時間の設定、mm:ss / dd:hh:mm:ss、終了状態の保持)、タイムゾーン処理に対応した固定日時までのカウントダウン Web コンポーネント、「開始までしばらくお待ちください」の配信用画面レイアウト(ブランドブロック + SNS ハンドル + ループするグラデーション背景)、および継ぎ目のないループ背景パターン。references/digit-flip.md— 決定論的な数字アニメーションコンポーネント 2 種類:フレーム駆動の縦型 0~9 ストリップを使うオドメーターリールと、上半分を折り返すフリップカード効果。ローンチ、セール、配信のどれに適しているかも説明します。