Communitygithub.com

Countdown-Video

Erstelle Countdown-Videos oder Livestream-Timer, die frame-genau bei Null enden – ohne Drift durch setInterval.

Was ist Countdown-Video?

Dieses Skill („Countdown-Video“) ermöglicht die Erstellung driftfreier Countdowns für Videos (Remotion → MP4) und Live-Webseiten. Der Kern des Skills ist das Prinzip, die verbleibende Zeit niemals mit `setInterval` zu dekrementieren, sondern sie in jedem Frame neu aus einem autoritativen Takt (z. B. `useCurrentFrame()` in Remotion oder `performance.now()` im Web) zu berechnen. Dadurch korrigieren sich späte Frames selbst, und der Timer landet exakt auf dem gewünschten Frame bei Null. Der Skill deckt Anwendungsfälle ab wie „Startet bald“-Bildschirme für Livestreams, Launch-Countdowns (dd:hh:mm:ss) und Flash-Sale-Timer (mm:ss). Er unterstützt die Plattformen Remotion (deterministisches Rendering) und Live-Webseiten (requestAnimationFrame). Die bereitgestellten Codebeispiele und Referenzdateien (`references/countdown-cookbook.md`, `references/digit-flip.md`) enthalten lauffähige Komponenten für die Zeitanzeige, Ziffern-Animationen (Odometrie, Flip-Card) und nahtlos schleifende Hintergründe. Eine Checkliste und ein Verifikationsprozess (Einzelbild-Rendering + Inspektion) stellen sicher, dass der Countdown frame-genau ist und das Layout nicht springt.

Funktioniert mit~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/iart-ai/tiktok-video-skills/tree/main/skills/countdown-video

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Was macht Countdown-Video?

Erstelle einen Countdown, der exakt dann endet, wenn er soll. Das gesamte Konzept basiert auf einer einzigen Idee: Leite die verbleibende Zeit aus dem Frame (oder einem monotonen Zeitstempel) ab, formatiere sie und animiere diese Zahl – zähle niemals mit einem setInterval herunter. Deckt „Startet bald“-Bildschirme für Livestreams, Launch-/Sale-Countdowns und sich wiederholende Hintergründe ab.

Wann verwenden

  • „Startet bald“ / „Bin gleich zurück“-Livestream-Bildschirme mit einem 5–10 Min. Timer.
  • Launch- und Flash-Sale-Countdowns (dd:hh:mm:ss bis zu einem Zieltermin; mm:ss für Dringlichkeit).
  • Jeder animierte Zähler, bei dem die Zahl exakt auf dem richtigen Frame Null erreichen muss.

Die eine Regel: Die Zahl von der Uhr ableiten, niemals von einem Tick

Ein setInterval(fn, 1000)-Countdown driftet – der Callback feuert mindestens 1000 ms später, nie exakt, und staut sich auf, wenn der Tab gedrosselt wird oder ein Render-Frame langsam ist. Berichte über etwa 1 Sekunde Verlust pro Minute sind üblich. Die Lösung ist für gerendertes Video und Live-Web gleich: Berechne die verbleibende Zeit als reine Funktion einer autoritativen Uhr, sodass ein später Frame sich im nächsten Frame selbst korrigiert, anstatt Fehler zu akkumulieren.

KontextAuthoritative UhrVerbleibende Zeit
Remotion-RenderuseCurrentFrame()gesamt − frame/fps
Web (Live-Seite)performance.now() oder Date.now()zielMs − jetzt
Bis zu einem festen DatumDate.now()zielTimestamp − Date.now()

Speichere die verbleibende Zeit niemals im State und dekrementiere sie. Berechne sie in jedem Frame neu.

Frame-genauer Countdown (Remotion → MP4)

useCurrentFrame() ist deterministisch: Frame N wird immer exakt bei N/fps Sekunden gerendert. Ziehe das von der Gesamtzeit ab und der Timer ist konstruktionsbedingt driftfrei.

import { useCurrentFrame, useVideoConfig } from "remotion";

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

  // verbleibende Sekunden, bei 0 geklammert – ceil, damit "1" für die gesamte Sekunde angezeigt wird
  const elapsed = frame / fps;
  const remaining = Math.max(0, Math.ceil(durationInSeconds - elapsed));

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

durationInSeconds ist eine Prop, keine Konstante – dieselbe Komposition rendert einen 5-Minuten-Stream-Timer oder einen 30-Sekunden-Sale-Timer, indem ein Eingabewert geändert wird. Setze durationInFrames der Komposition auf durationInSeconds * fps, damit das Video genau so lang ist wie der Countdown.

Web-Timer (Live-Seite, requestAnimationFrame)

Treibe für eine Live-Seite eine rAF-Schleife mit performance.now() an und male nur neu, wenn sich die angezeigte Sekunde ändert (keine 60×/s Arbeit für eine 1-Hz-Zahl):

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);

Das Herunterzählen auf ein festes Datum funktioniert mit Date.now() nach dem gleichen Prinzip: Math.max(0, target - Date.now()). Siehe references/countdown-cookbook.md für die vollständige Datumsziel-Version mit Zeitzonenbehandlung.

Zeitformatierung

Zerlege einen Integer der verbleibenden Sekunden mit Division + Modulo in Felder und fülle mit Nullen auf. Wähle das Format passend zur Dauer – zeige niemals 00d 00h 05m an, wenn nur Minuten zählen.

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)}`;
};
FormatVerwendungFelder ausblenden, wenn
mm:ssStream „Startet bald“, kurze Timer (<1h)—
hh:mm:ssFlash-Sale, gleichtägiges Eventdd ausblenden, solange 0
dd:hh:mm:ssLaunch / mehrtägiger CountdownFührende Null-Einheiten für sauberere Darstellung ausblenden

Setze immer font-variant-numeric: tabular-nums (oder eine monospaced Ziffernschrift), damit die Ziffern gleich breit bleiben und das Layout beim Zahlenwechsel nicht springt.

Ziffern-Roll-/Flip-Übergänge

Ein einfacher Zahlenwechsel wirkt billig; der rollende „Kilometerzähler“-Look verkauft den Countdown. Rendere jede Ziffer als vertikalen Streifen 0–9 und verschiebe ihn um -Ziffer × ZifferHöhe. Animiere die Position, nicht den Textinhalt, sodass die Änderung ein Gleiten ist, kein Sprung.

.reel { height: 1em; overflow: hidden; }
.reel .col { transition: transform .45s cubic-bezier(.22,1,.36,1); }
/* col enthält gestapelte 0..9; um -Wert em verschieben */

In Remotion treibe den Strip-Offset über den Frame anstelle eines CSS-Übergangs (damit er deterministisch ist). Der Flip-Card-Stil (obere Hälfte klappt über die untere) und die Ziffern-Rolle werden detailliert mit lauffähigen Komponenten in references/digit-flip.md beschrieben.

Hintergrund-Schleife

Stream-Bildschirme bleiben Minuten lang sichtbar, daher muss der Hintergrund nahtlos schleifen. Baue eine Bewegung auf einem Zyklus auf, der gleichmäßig in die Timeline passt (z. B. ein Verlaufs- oder Partikeldrift, dessen Periode loopFrames ist), und greife darauf mit frame % loopFrames zu, sodass an der Nahtstelle kein sichtbarer Übergang entsteht. Halte ihn kontrastarm und langsam – die Zahl ist das Subjekt; der Hintergrund ist Atmosphäre. Rezepte in references/countdown-cookbook.md.

Ausgabe-Checkliste

  • Die verbleibende Zeit wird aus useCurrentFrame() / performance.now() in jedem Frame neu berechnet – keine setInterval-Dekrementierung.
  • Der Timer erreicht 00:00 auf dem exakt vorgesehenen Frame; durationInFrames === durationInSeconds × fps.
  • Die Dauer ist eine Eingabe-Prop, keine fest codierte Konstante.
  • Ziffern verwenden tabular-nums; das Layout verschiebt sich beim Zahlenwechsel nicht.
  • Das Format passt zur Spanne (mm:ss vs. dd:hh:mm:ss); führende Null-Einheiten werden weggelassen, wenn sie ablenken.
  • Die Hintergrund-Schleifenperiode teilt die Timeline gleichmäßig; keine Naht an der Übergangsstelle.

Ausliefern & verifizieren (gerenderte Einzelbilder → MP4)

Verpackter Helfer (scripts/): Lege deine Einzelbilder mit scripts/contact-sheet.sh sheet.png f-hook.png f-mid.png f-end.png als Kontaktbogen an und überprüfe die Kodierung mit scripts/probe-mp4.sh out.mp4 [WxH] [fps]. Siehe scripts/README.md.

Der Countdown wird als Remotion-Komposition ausgeliefert (<Composition> + zod schema + defaultProps, durationInSeconds als Prop) – der Timer ist eine reine Funktion von useCurrentFrame(), niemals setInterval / Date.now() / Math.random(). Setze durationInFrames = durationInSeconds * fps. Auslieferung = out/*.mp4 + das Projekt. 9:16 hochkant (1080×1920) ist der Standard.

Verifikationsschleife – Einzelbilder rendern → prüfen → kodieren. Der ganze Sinn ist Frame-Genauigkeit, also prüfe den Ziffernwert an exakten Frames vor der Kodierung.

# Einzelbilder am Anfang / Mitte / Ende – MIT DEN AUSGELIEFERTEN PROPS (der tatsächlichen Dauer, die du rendern wirst)
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

# Prüfe jedes PNG – die Ziffern müssen EXAKT der Frame/fps-Mathematik entsprechen:
#  - bei Frame N wird angezeigt = ceil(durationInSeconds - N/fps)  (z.B. 300s bei 30fps, Frame 4500 → 150s → "02:30")
#  - letzter Frame zeigt 00:00 (oder den Endzustand); kein Drift, kein Off-by-one-Sekunde
#  - tabular-nums: Das Layout springt nicht zwischen den Frames
#  - 9:16 sicherer Bereich: Timer + Markenblock liegen klar außerhalb der oberen ~12% und unteren ~20-35% UI sowie der rechten Aktionsschiene

npx remotion render Countdown out/countdown.mp4 --props='{"durationInSeconds":300}'   # kodieren, sobald die Ziffern geprüft sind
npx remotion render Countdown out/demo.gif --codec=gif                                 # README-Beweisclip

Verwende npx remotion compositions, um durationInFrames/fps zu bestätigen, und berechne dann den Frame für jede gewünschte Sekunde, die du prüfen möchtest.

Bevor du fertig bist:

  1. Einzelbilder werden sauber bei Frame 0, Mitte und letztem Frame gerendert – keine Fehler.
  2. Der Ziffernwert bei Frame N ist exakt gleich ceil(durationInSeconds - N/fps); letzter Frame erreicht 00:00 auf dem vorgesehenen Frame (kein Drift / Off-by-one).
  3. Timer + Marke bleiben bei jedem geprüften Frame innerhalb des 9:16-Sicherheitsbereichs.
  4. Nur Frame-gesteuert – kein setInterval / Date.now() / Timer; tabular-nums, damit das Layout hält.
  5. Die ausgelieferte durationInSeconds (nicht nur defaultProps) wird korrekt gerendert; MP4 kodiert, GIF optional.

Referenzdateien

  • references/countdown-cookbook.md – vollständige lauffähige Remotion <Countdown> (konfigurierbare Dauer, mm:ss / dd:hh:mm:ss, Endzustand-Halte), die Countdown-bis-zu-einem-festen-Datum-Webkomponente mit Zeitzonenbehandlung, ein „Startet bald“-Stream-Bildschirmlayout (Markenblock + Social-Media-Links + sich wiederholender Verlaufshintergrund) und das nahtlose Schleifen-Hintergrundmuster.
  • references/digit-flip.md – zwei deterministische Ziffernanimationskomponenten: die Kilometerzähler-Rolle (vertikaler 0–9-Streifen, vom Frame gesteuert) und der Flip-Card-Effekt (obere Hälfte faltet sich), plus wann welche für Launch vs. Sale vs. Stream geeignet ist.

Individual skills in this repo

This repo contains 3 individual skills — each has its own dedicated page.

Verwandte Skills