Skip to content
Entertainment
Skill

/promo-video

Record a high-FPS autonomous promo video of a Phaser game using Playwright. Triggers on: promo video, gameplay recording, marketing video, game capture.

BOOST
From plugin
game-creator
34531 skills4 agents
Install
$ npx -y skills add playableintelligence/game-creator --skill promo-video --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/promo-video

Context preview

The summary Claude sees to decide when to auto-load this skill.

Record a high-FPS autonomous promo video of a Phaser game using Playwright. Triggers on: promo video, gameplay recording, marketing video, game capture.

SKILL.md

promo-video.SKILL.md
name: promo-video
description: >
  Record a high-FPS autonomous promo video of a Phaser game using Playwright.
  Triggers on: promo video, gameplay recording, marketing video, game capture.
argument-hint: "[path-to-game]"
license: MIT
compatibility: Requires Playwright and FFmpeg installed locally for video capture and encoding.
metadata:
  author: OpusGameLabs
  version: 1.3.0
  tags: [game, promo, video, recording, playwright, ffmpeg]

Promo Video Recording

Record smooth, autonomous promo footage of a Phaser game for marketing / social media. The output is a 50 FPS MP4 in mobile portrait (9:16) — ready for TikTok, Reels, Moltbook, or X.

Technique

Playwright's `recordVideo` caps at 25 FPS with no config option. We work around it:

1. **Slow the game to 0.5×** by patching all 5 Phaser time subsystems 2. **Record for 2× the desired duration** at Playwright's native 25 FPS 3. **FFmpeg speed-up 2×** → effective 50 FPS output

| Parameter | Default | Effect | | ----------------------- | ---------------------------------------- | --------------------------------------------------------------------- | | `SLOW_MO_FACTOR` | `0.5` | Game runs at half speed → 50 FPS output | | `WALL_CLOCK_DURATION` | `DESIRED_GAME_DURATION / SLOW_MO_FACTOR` | Record for 2× to get correct game-time | | `VIEWPORT` | `{ width: 1080, height: 1920 }` | 9:16 mobile portrait (always default unless user specifies otherwise) | | `DESIRED_GAME_DURATION` | `13000` (ms) | ~13s of game-time → ~6.5s promo clip |

Prerequisites

  • **Playwright** — must be installed (`npm install -D @playwright/test && npx playwright install chromium`)
  • **FFmpeg** — must be available on PATH (`brew install ffmpeg` on macOS)
  • **Dev server running** — game must be served on localhost

Check both before starting:

npx playwright --version
ffmpeg -version | head -1

If FFmpeg is not found, warn the user and skip the promo video step (it's non-blocking — the game still works without it).

Capture Script — Game-Specific Adaptation

Every game gets a custom `scripts/capture-promo.mjs`. The subagent **must read the game's source files** to determine:

1. Death/Failure Patching (CRITICAL)

The video must show continuous gameplay — never game over. Read `GameScene.js` (or equivalent) to find the death/failure method and monkey-patch it out.

**How to find it**: Search for the method called on collision/death. Common patterns:

  • `this.triggerGameOver()` — dodge games
  • `this.takeDamage()` → `this.lives <= 0` — multi-life games
  • `this.gameOver()` — direct call
  • `eventBus.emit(Events.PLAYER_HIT)` / `eventBus.emit(Events.GAME_OVER)` — event-driven

**Patch template** (adapt per game):

await page.evaluate(() => {
  const scene = window.__GAME__.scene.getScene("GameScene");
  if (scene) {
    // Patch ALL paths to game over
    scene.triggerGameOver = () => {};
    scene.onPlayerHit = () => {};
    // For multi-life games, also prevent damage:
    // scene.takeDamage = () => {};
    // scene.playerDied = () => {};
  }
});

2. Input Sequence Generation

The video must show dynamic, natural-looking gameplay. Read the game's input handling to determine:

  • **Which keys** — ArrowLeft/ArrowRight? Space? WASD? Mouse clicks?
  • **Input style** — continuous hold (movement), tap (jump/shoot), or both?
  • **Movement pattern** — should the player sweep across the screen, dodge reactively, jump rhythmically?

**Input patterns by game type:**

| Game Type | Input Keys | Pattern | | ------------------- | ----------------------- | -------------------------------------------------------------------------- | | Side dodger | ArrowLeft, ArrowRight | Alternating holds (150-600ms) with variable pauses, occasional double-taps | | Platformer / Flappy | Space | Rhythmic taps (80-150ms hold) with variable gaps (200-800ms) | | Top-down | WASD / Arrows | Mixed directional holds, figure-eight patterns | | Shooter | ArrowLeft/Right + Space | Movement interleaved with rapid fire | | Clicker/Tapper | Mouse click / Space | Rapid bursts separated by brief pauses |

**Randomize timing** to avoid robotic-looking movement:

const holdMs = 150 + Math.floor(Math.random() * 450);
const pauseMs = 50 + Math.floor(Math.random() * 250);

**Add a pause at the start** (1-2s) to let the entrance animation play — this is the hook.

3. Game Boot Detection

All Phaser games built with the `/viral-game` or `/make-game` pipelines expose these globals:

  • `window.__GAME__` — Phaser.Game instance
  • `window.__GAME_STATE__` — GameState singleton
  • `window.__EVENT_BUS__` — EventBus singleton

Wait for both boot and active gameplay:

await page.waitForFunction(() => window.__GAME__?.isBooted, { timeout: 15000 });
await page.waitForFunction(() => window.__GAME_STATE__?.started, {
  timeout: 10000,
});

4. Time Scaling Injection

Slow all 5 Phaser time subsystems for the recording:

await page.evaluate(
  ({ factor }) => {
    const game = window.__GAME__;
    const scene = game.scene.getScene("GameScene");

    // 1. Update delta — slows frame-delta-dependent logic
    const originalUpdate = scene.update.bind(scene);
    scene.update = function (time, delta) {
      originalUpdate(time, delta * factor);
    };

    // 2. Tweens — slows all tween animations
    scene.tweens.timeScale = factor;

    // 3. Scene timers — slows scene.time.addEvent() timers
    scene.time.
Read more
Ships withgame-creator

Go from game idea to deployed, monetized browser game in minutes — not hours. Tell your AI coding agent "make a space invaders game" and get a fully playable 2D or 3D browser game with pixel art, procedural audio, and automated QA. Deploy instantly to the web.

Get the whole plugin

Other skills on game-creator.