Skip to content
Back to skills

Cinematic Cutscene

ASecurity

Generate a non-interactive cutscene: a reference-locked 5-10s video shot with optional voiced dialogue.

  • 77 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 16, 2026
ai-agentsgoshellreactnodeaws

Works with

  • claude code
  • cursor
  • cli
  • mcp

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned October 16, 2026

npx -y skills add SummerEngine/summer --skill cinematic-cutscene --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Cinematic Cutscene?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Cinematic Cutscene
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/summerengine-cinematic-cutscene/badge)](https://www.skillsdirectory.com/skills/summerengine-cinematic-cutscene)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: cinematic-cutscene
description: "Generate a non-interactive cutscene: a reference-locked 5-10s video shot with optional voiced dialogue."
license: MIT
compatibility: [Cursor, Claude Code, Windsurf, Codex]
category: video
user-invocable: true
allowed-tools: Read Grep Glob Write Edit summer_assets_generate_video summer_jobs_wait summer_assets_generate_image summer_assets_generate_audio summer_assets_search summer_import_from_url summer_add_node summer_set_prop summer_call
paths: ["assets/video/**", "cinematics/**", "trailers/**"]
---

# cinematic-cutscene — Locked-Look Story Beat in 5-10 Seconds

A cutscene is a non-interactive video clip the game plays back at a fixed moment — opening, story beat, character intro, ending. Quality is dominated by **two things**: continuity (the character on screen looks like the character in the game) and shot length (every clip past 10s drifts in identity, hands, and physics). This skill enforces both — generate a reference image first to lock the look, then image-to-video each shot, then chain shots in sequence rather than asking the model for one long take.

If the user wants 5 seconds of marketing footage, that's `trailer-shot`. If they want a seamless background loop, that's `animated-loop`. This skill is for narrative beats with a defined start, middle, and end — usually with dialogue or VO.

## When to use

- "Generate the opening cinematic for the game."
- "I need a cutscene where the witch turns to the player and warns them about the curse."
- "Make a 10-second character intro for the boss."
- "Add an ending sequence after the player defeats the dragon."
- "Cinematic where the village burns and the protagonist runs."
- The user has a defined moment with story content — not a vibe loop, not a marketing splash.

## When NOT to use

- "5-second slow-mo combat shot for the trailer" — `trailer-shot`.
- "Looping splash screen background" — `animated-loop`.
- "Animated logo backdrop on the title screen" — `animated-loop`.
- Real-time in-engine cinematic with the actual gameplay characters and camera — that's an AnimationPlayer job, not generated video. Route to `scene-composition`.
- Voice-only narration over a static image — generate the dialogue with `assets.generate_audio` and use a static `TextureRect`, no video needed.

## Steps

### 1. Read the soul file and any prior cutscenes

```
Read .summer/GameSoul.md
assets.search({ query: "<character name> reference", assetType: "2d_image", scope: "all" })
```

`assets.search` has no video filter — `assetType` is one of `2d_image`, `animation`, `3d_model`, `audio`, `music`, `all`, and there is no video member. To find prior cutscene clips, search your own assets with `assets.search({ query: "cutscene", scope: "mine" })` and read the titles.

If a reference image of the character already exists from a prior `concept-art` or `character-portrait` run, **reuse it** as the `imageUrl` — that single decision is the difference between the character looking like themselves and looking like a stranger.

### 2. Plan the shot list

Cutscenes longer than 10 seconds are **multiple shots**, not one take. Ask the user to break it down:

> Want this as one 10s shot, or three shots (e.g. wide → close on face → reaction)? Each shot is 5-10s. I'll generate a reference image once, then image-to-video each shot off of it.

Default if the user is vague: **3 shots, 5s each, on kling**. Confirm before spending credits: `dryRun: true` on a shot's call returns its `estimate` (credits, balance, enough) and renders nothing.

### 3. Lock the look — generate a reference image first

For any character or hero scene, generate a reference still with `assets.generate_image` *before* generating video. This still drives `imageUrl` on every subsequent shot, so the character is consistent across cuts.

```
assets.generate_image({
  prompt: "<subject>, <setting>, cinematic lighting, film still, wide framing, <art style from GameSoul.md>",
  model: "nano-banana-2",
  aspectRatio: "16:9"
})
```

Set `aspectRatio` to the cutscene's framing (`16:9`, or `9:16` for mobile); `width` and `height` ask for a minimum size. For image-to-video this matters more than usual: the clip's framing is inherited from the reference, so a square reference gives you a square cutscene.

Show the user the still and confirm it's the right look before video-ing it. Regenerating a still costs far less than regenerating a video.

### 4. Pick the model

**This skill always passes `imageUrl`, which puts every call on the image-to-video route.** `kling-turbo` and `veo3` are text-to-video only; asking for either with an `imageUrl` set is a 400 `invalid_model`, not a silent downgrade.

| Model | i2v? | Speed | When |
|---|---|---|---|
| `ltx` | yes | ~30s | Iteration, blocking shots, B-roll, throwaway tests |
| `kling` | yes | 2-4 min | Hero shots, character cutscenes, anything the player will sit and watch |
| `kling-turbo` | **no** | 1-2 min | Text-to-video only — usable only if you drop the reference image |
| `veo3` | **no** | 3-5 min | Text-to-video only — usable only if you drop the reference image |

Default policy: **`ltx` first** to validate the prompt and shot framing. If it lands the composition but quality is rough, escalate to `kling`. `veo3` is a real tradeoff, not an upgrade: reaching for it means giving up `imageUrl`, which is the one thing that holds character identity across shots. Take that trade only when on-camera lip-sync matters more than continuity, and only with the user's approval on cost.

### 5. Generate each shot

```
assets.generate_video({
  prompt: "<subject does <action>, <camera move>, <lighting>, cinematic, 16mm film grain>",
  model: "kling",
  imageUrl: "<reference image fileUrl from step 3>",
  duration: 5,
  aspectRatio: "16:9"
})
```

On `ltx` and `kling`, `duration` is 5 or 10 — anything else renders 5. The newer models in the `model` list render their nearest supported length.

`assets.generate_video` always answers at once with a job — `{ status: "running", job: { id } }` and **no `fileUrl`**; the clip is rendered by a background worker. Wait for it:

```
jobs.wait({ jobId: "<job.id from the generate call>", timeoutSeconds: 50 })
```

Call `jobs.wait` again until `finished` is true. Only a finished job with state `done` has the asset and URL in its `result`. Do not tell the user a shot is done off the back of the generate call — at that moment nothing has rendered yet.

`aspectRatio` is applied on the text-to-video path only; on image-to-video the framing comes from the reference image, so a `9:16` cutscene needs a `9:16` reference, not a `9:16` argument.

When the job completes, show the user the URL and ask:

> Shot 1 of 3 done. Land or regenerate? If land, I'll move to shot 2.

### 6. Generate dialogue audio (if the scene has dialogue)

Cutscene dialogue is TTS, not in the video model. The video model can render mouth motion that *looks* like talking, but the audio comes from `assets.generate_audio`. Generate it separately and an AnimationPlayer or your `cinematics/` controller scene syncs them.

```
assets.generate_audio({
  capability: "text_to_speech",
  text: "They'll come for you at dawn. Run while you can.",
  voiceId: "<from audio bible — see voice-line>"
})
```

Lip-sync caveat: if the video shows the character clearly mouthing words and the audio is a different cadence, viewers notice. Either (a) keep the camera off the character's face during dialogue, (b) accept the asynchrony for a stylized look, or (c) use `veo3` and prompt the dialogue text directly into the video prompt for synced motion.

### 7. Import and wire as a VideoStreamPlayer

**Transcode to Ogg Theora first.** The generated clip is an `.mp4`, and Summer Engine cannot load `.mp4` — the only video container in the build is `.ogv`. Measured against the shipped binary (4.6.1):

```
$ Summer --headless --path <proj> -s res://probe.gd
VideoStream exts: ["ogv", "tres", "res"]
ERROR: No loader found for resource: res://clip.mp4 (expected type: unknown)
```

Nothing transcodes for you — `summer_import_from_url` downloads the bytes, runs the engine's import pipeline, and *fails the import* if the file never becomes a loadable resource. Convert with ffmpeg or DaVinci before importing:

```
ffmpeg -i intro_shot_01.mp4 -c:v libtheora -q:v 8 -an intro_shot_01.ogv
```

Then:

```
summer_import_from_url(url="<hosted .ogv url>", path="res://assets/video/cinematics/intro_shot_01.ogv")
```

Build a controller scene at `res://cinematics/Intro.tscn`: create it, then build its nodes in one script (`scenePath` names the new scene; the editor's open tab comes back afterwards):

```
summer_create_scene(path="res://cinematics/Intro.tscn", rootName="Intro", rootType="Control")
summer_run_script(scenePath="res://cinematics/Intro.tscn", source="""
func run(ctx):
    var video = ctx.add_node("VideoStreamPlayer", "Video", null, {
        "stream": load("res://assets/video/cinematics/intro_shot_01.ogv"),
        "autoplay": true,
        "expand": true,
    })
    video.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
    var fade = ctx.add_node("ColorRect", "Fade", null, {"color": Color(0, 0, 0, 1)})   # black, alpha 1.0 -> 0.0
    fade.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
    fade.mouse_filter = Control.MOUSE_FILTER_IGNORE
    ctx.save_scene()
""")
```

`Fade` comes after `Video` in the tree, so it draws on top of it.

Attach a `Tween` script that fades the `ColorRect` from black to clear over 0.5s on `_ready`, then back to black when `Video.finished` fires, then `queue_free()`s the scene. For multi-shot cutscenes, queue the next `VideoStreamPlayer` in the `finished` signal handler.

For dialogue, add an `AudioStreamPlayer` sibling with the TTS clip and call `play()` in `_ready` after a small delay matching where the line lands in the video.

## Reference card — prompts that work

Pattern: `<subject> + <action> + <camera move> + <lighting> + <stylistic anchor>`. Keep prompts under 50 words; over-prompting confuses the model. Always pair with `imageUrl` to lock the character.

| Goal | Model | Prompt | Duration |
|---|---|---|---|
| Opening establishing shot | `kling` | `wide shot of a fog-shrouded medieval village at dawn, slow dolly-in toward the church spire, warm low sun, cinematic, anamorphic lens flare` | 5s |
| Character intro (hero turns to camera) | `kling` | `young witch with raven on shoulder turns slowly toward camera, candlelit interior, shallow depth of field, cinematic film still` | 5s |
| Dialogue close-up (no synced lips) | `kling` | `close-up of a grizzled knight, eyes downcast then looking up, firelight on his face, dust motes, 16mm film grain` | 5s |
| Dialogue close-up (synced lips) | `veo3` — text-to-video only, drop `imageUrl` | `close-up of the witch saying "they will come for you at dawn", candlelight, cinematic, shallow DOF` | 5s |
| Action beat (village burns) | `kling` | `medium wide of a thatched village engulfed in flames at night, embers rising, silhouettes running through smoke, hand-held camera, cinematic` | 5s |
| Ending shot (hero walks away) | `kling` | `low wide shot of a cloaked figure walking away across a blasted plain at sunset, slow truck-back, golden hour, dust on the wind` | 5s |
| Throwaway iteration / blocking | `ltx` | same prompt as above | 5s |
| Anime / stylized cutscene | `kling` | `anime-style young swordsman draws blade and steps forward, sakura petals, dramatic wind, Studio Ghibli soft painterly` | 5s |

### Bad prompts and why

| Bad | Why it fails |
|---|---|
| `epic cutscene of the hero defeating the boss` | No subject, no shot, no camera. Returns a generic action montage. |
| `the hero walks into the throne room, kneels before the king, draws his sword and says I refuse to serve, then walks out` | Five events in one prompt. Model picks one (badly) or tries all and renders mush. Split into three shots. |
| `cinematic 4k masterpiece trending on artstation` | Adjective slop. The model already knows "cinematic"; the rest is dead weight. |
| `make the character look exactly like in the game` | Words can't anchor identity. Use `imageUrl`. |
| `the camera does a complex handheld weaving move through the crowd` | Video models render simple camera moves (pan, tilt, dolly, truck) reliably and complex moves badly. Pick one verb. |

## Anti-patterns

- **Generating a 10s shot when you should chain two 5s shots.** Identity, hand consistency, and physics drift every additional second past ~6s. Two 5s shots cut together look better and cost the same.
- **Skipping the reference image.** Generating four character cutscenes from text prompts alone produces four different-looking people. Always lock the look with `assets.generate_image` first, then `imageUrl` every subsequent video call.
- **Asking the video model to render dialogue without `veo3`.** `kling` and `ltx` will animate mouths but the motion does not match any audio. Use `veo3` if dialogue is on-camera, or keep the camera off the speaker.
- **Using `kling` for blocking iterations.** Validate framing and prompt on the cheaper `ltx` first; move to `kling` once the composition lands.
- **Putting the cutscene on a `VideoStreamPlayer` without a fade.** Cuts straight from gameplay to video are jarring. Always wrap in a `ColorRect` fade in/out.
- **Treating `assets.generate_video` as finished.** It answers `{ status: "running", job }` and no URL. Wait with `jobs.wait` until the job is done before there is anything to show or import.
- **Importing the `.mp4` as-is.** The engine has no MP4 or WebM loader — only Ogg Theora (`.ogv`). Transcode first; an un-transcoded import fails and is rolled back.
- **Asking `ltx` or `kling` for `duration: 3` or `duration: 8`.** On those two only 5 and 10 exist; anything else renders 5.
- **Pairing `imageUrl` with `kling-turbo` or `veo3`.** Neither has an image-to-video endpoint — that combination is a 400.

## Edge cases

- **Vertical cutscene for a mobile target.** On the image-to-video path `aspectRatio` is not applied at all — the clip inherits the reference image's framing. So the reference *must* be 9:16: generate it with `aspectRatio: "9:16"`, then pass its URL as `imageUrl`.
- **Cutscene must match an in-engine character precisely.** No video model will hit pixel-perfect identity. Either (a) accept the artistic license, (b) render the cutscene in-engine with `AnimationPlayer` instead of generating it, or (c) generate a reference image *from* a screenshot of the in-engine character (image-to-image first, then image-to-video).
- **Dialogue is too long for a 5s shot.** Either trim the line, or split: 5s of speaker, 5s of listener reaction (cheaper because the listener doesn't need synced lips).
- **Cutscene needs to play and the player isn't looking.** Pause the gameplay timer, push a `Control` overlay with the `VideoStreamPlayer`, restore on finish.
- **Multi-shot continuity.** Use the same reference image as `imageUrl` across all shots. If lighting differs by shot, generate one reference per lighting setup, not one per shot.

## Without Summer Engine

In a session without Summer Engine tools (the assets profile; the `summer-assets` skill has the whole loop), do steps 1-6 and skip step 7 (`VideoStreamPlayer`). Skip the soul-file read in step 1 when the project has no `.summer/` folder. Save the file where the person asked with `out` (the `summer-assets` skill says how), and tell them the path and format. Each shot is an MP4 and each dialogue line an audio file; the person cuts them together in their own editor.

## Fallback (no MCP)

If the MCP server isn't running, the user can do all of this in the Summer dashboard:

1. Generate a reference image in the Image tab.
2. Open the Video tab, paste the reference URL into the image-to-video field.
3. Set model, duration, aspect ratio, and prompt as listed above.
4. Download the `.mp4`, transcode it to Ogg Theora (`ffmpeg -i in.mp4 -c:v libtheora -q:v 8 -an out.ogv`), and drop the `.ogv` into `assets/video/cinematics/`. Summer Engine has no MP4 or WebM loader — `.ogv` is the only video container it can open.

Print the exact prompt + model + duration + aspect ratio so the user can paste it into the dashboard verbatim.

## Handoff

Once the cutscene is generated and wired:

> Cutscene `intro` wired at `cinematics/Intro.tscn` with fade in/out and `Video.finished` chained to scene change. Next:
> - Add the dialogue track with `voice-line` if you haven't yet.
> - Score the moment with `music-track` — a cutscene without music feels like a placeholder.
> - For the boss reveal cutscene, run this skill again with the same reference image to keep the witch identity stable.
> - If you want a 5s "money shot" version for marketing, run `trailer-shot` against the same reference.

## See also

- `trailer-shot` — marketing footage, 5-10s, maximum visual punch.
- `animated-loop` — seamless looping background clips.
- `voice-line` — TTS dialogue used inside cutscenes.
- `music-track` — score the cutscene.
- `concept-art` — generate the reference image axis if no character reference exists yet.
- `character-portrait` — produce a high-fidelity locked character portrait for use as `imageUrl`.
- `summer_docs_read` id `reference/mcp-tools-reference` — how to call `assets.generate_video`; `summer_search` returns its exact input.

<!-- shell-commands: generated from the operation catalog by npm run generate:registry; do not edit by hand -->
## From a shell

No Summer MCP tools in this session? Run each one as a command; when `summer --version` is missing or below 4.0, use `npx -y summer-engine@latest` in place of `summer`. Fields are flags in kebab-case (`--scene-path res://main.tscn`) or one `--input '<json>'`, and `summer ops describe <op>` prints the exact input. The `summer-cli` skill has the rest.

| In this skill | Command |
|---|---|
| `assets.generate_audio`, `summer_assets_generate_audio` | `summer assets generate-audio` |
| `assets.generate_image`, `summer_assets_generate_image` | `summer assets generate-image` |
| `assets.generate_video`, `summer_assets_generate_video` | `summer assets generate-video` |
| `assets.search`, `summer_assets_search` | `summer assets search` |
| `summer_docs_read` | `summer docs read` |
| `summer_add_node` | `summer engine add-node` |
| `summer_create_scene` | `summer engine create-scene` |
| `summer_import_from_url` | `summer engine import-from-url` |
| `summer_run_script` | `summer engine run-script` |
| `summer_set_prop` | `summer engine set-prop` |
| `jobs.wait`, `summer_jobs_wait` | `summer jobs wait` |
| `summer_search` | `summer ops search "<what you want>"` |
<!-- /shell-commands -->

Files in this skill

  • SKILL.md18.3 KB
  • resource.yaml738 B

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.