Poetry Cinema Page
ASecurityTurn any Chinese classical poem, ci, qu, or prose-poem into a cinematic, immersive scroll-driven webpage with historically and emotionally appropriate imagery generated by either Volcengine Ark (火山引擎方舟) Doubao Seedream 5.0 or a third-party GPT-Image gateway — selectable per tier and mixable within one poem — where the hero image is reused as an image-to-image reference so every later beat inherits the same world, plus a narration track that is generated rather than supplied — locally with edg...
- 277 stars
- 0 votes
- 0 copies
- 1 view
- Added September 25, 2026
Works with
Security analysis
92/100- Installs packages at runtime which could introduce malicious dependencies
Pro scans all 20 files and shows the line behind each finding
npx -y skills add wwwzhouhui/skills_collection --skill poetry-cinema-page --agent claude-codeAre you the author of Poetry Cinema Page?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/wwwzhouhui-poetry-cinema-page)---
name: poetry-cinema-page
description: Turn any Chinese classical poem, ci, qu, or prose-poem into a cinematic, immersive scroll-driven webpage with historically and emotionally appropriate imagery generated by either Volcengine Ark (火山引擎方舟) Doubao Seedream 5.0 or a third-party GPT-Image gateway — selectable per tier and mixable within one poem — where the hero image is reused as an image-to-image reference so every later beat inherits the same world, plus a narration track that is generated rather than supplied — locally with edge-tts (free, no key) or through the same Ark key with Volcengine Doubao seed-tts — plus line-by-line interpretation, visual storytelling, responsive design, and browser QA, and an optional Remotion-rendered voiceover video (口播视频) that pins the generated scene images and hard-cut subtitles onto the narration timeline. Use when the user supplies ancient Chinese poetry and asks for an atmospheric page, interactive appreciation experience, visual interpretation, educational poem website, image-led literary presentation, a spoken recitation/voice-over of the poem, or asks to turn the generated page/images/narration into a narrated video.
---
# Poetry Cinema Page
Create a complete webpage, not a mockup. Preserve the poem exactly, generate a coherent image set, implement scroll-driven scene transitions, explain the poem clearly, build the project, and visually inspect it.
## Non-negotiable quality bar
- Treat imagery as the poem's emotional world, not decoration.
- Prefer cinematic realism, high-end historical photography, crafted diorama realism, or restrained ink-inspired cinematography according to the poem. Never default to low-poly, cartoon, plastic-toy, or generic “ancient style.”
- Use one visual bible across every generated image: same geography, characters, era, palette, materials, weather, camera language, and time progression.
- Keep image files free of text, calligraphy, logos, seals, signatures, and watermarks. Add typography in HTML.
- Never replace or overwrite an existing poetry page unless the user explicitly asks. Create a new route/page and link the pages.
- State textual variants instead of silently “correcting” the user's poem.
## Required workflow
### 1. Inspect the project
Identify the framework, build scripts, existing pages, design system, dev-server state, and dirty files. Reuse the current stack. For Vite multi-page sites, add a new HTML entry and configure `build.rollupOptions.input` when needed.
### 2. Build the literary scene map
Read [references/poetry-analysis.md](references/poetry-analysis.md). Produce internally:
- verified title, author, era, genre/ci tune;
- 5–8 visual beats based on imagery, action, time, and emotional turns—not equal line counts;
- for every beat: original text, plain meaning, core images, technique, emotional function, and proposed scene;
- the overall arc, such as `landscape → history → hero → self → release`.
Use the supplied text verbatim in the page. If a common textual variant matters, mention it in explanatory copy without derailing the experience.
### 3. Choose an art direction
Read [references/image-direction.md](references/image-direction.md). Select a visual form from the poem itself:
- intimate travel, village, still life → realistic handcrafted diorama or macro historical photography;
- war, rivers, mountains, political history → large-scale prestige historical cinema;
- dream, myth, celestial, highly metaphorical work → restrained painterly cinema with physically credible light;
- quiet philosophical poem → minimal photographic landscape with negative space.
Default to a hero plus one image per major beat. Six to eight total images usually balances immersion, consistency, and load time.
### 4. Generate and persist the image set
Images are generated through the bundled CLI `scripts/ark_image.py`, which can talk to either Volcengine Ark (火山引擎方舟) Doubao Seedream 5.0 or a third-party GPT-Image gateway — two providers configured side by side and selected per tier. One call per distinct asset; never one generic prompt for all images. Full parameter reference: [references/ark-api.md](references/ark-api.md).
Verify credentials and endpoint before spending any generation time:
```bash
python scripts/ark_image.py probe
```
`probe` resolves the key of every provider (see [Configuration](#configuration)), reports each endpoint with its models, and then verifies every configured model id by sending one request that is rejected on purpose by validation — no image is generated and nothing is billed. Ark model ids are fully proved this way, because Ark validates `size` and resolves the model too; for `gpt-2` / `gpt-2.5` the report says `model id NOT verified; use --live for that`, since the gateway validates the request body before the model — only `probe --live`, which does generate one image per model, proves those ids. Fix credentials before continuing; do not start a poem on an untested key.
Pick the tier by role. The tier is the only thing that selects a provider, so mixing providers inside one poem is normal. `pro` (`doubao-seedream-5.0-pro`) and `lite` (`doubao-seedream-5.0-lite`) are Volcengine Ark: **hero and every beat use `pro`** (highest quality), **drafts and repairs use `lite`** (cheaper and faster). `gpt-2` (`gpt-image-2`) and `gpt-2.5` (`gpt-image-2.5`) are the GPT-Image gateway — use them when you deliberately want that provider for the hero or for individual beats. Role routing lives in the config file, so `--role` is normally the only selector needed; override per call with `--tier`, `--provider`, or `--model`.
Generate the hero with text-to-image:
```bash
python scripts/ark_image.py t2i \
--role hero \
--size 2560x1440 \
--prompt "<hero paragraph>" \
--out public/generated/<poem-slug>/hero.jpg
```
Then generate every beat with image-to-image, passing the hero as a reference:
```bash
python scripts/ark_image.py i2i \
--role beat \
--ref public/generated/<poem-slug>/hero.jpg \
--prompt "<beat paragraph>" \
--out public/generated/<poem-slug>/scene-1.jpg
```
The hero reference is what makes the set one world: geography, period, recurring characters, wardrobe, materials, palette, and lens language come from the reference image instead of from prose alone. The chain also crosses providers — an Ark hero can be the reference for a beat generated on the gateway, and the reverse works too. When a beat introduces a new recurring character, generate that character deliberately, then pass its file as an extra `--ref` for later beats that include it. Reserve negative space on the side where page copy will sit.
Never move a reference call to the gateway's `/images/generations` endpoint: the `image`, `image_url` and `images` fields are silently ignored there, so the call returns 200 with a fresh text-to-image picture and the reference chain is quietly gone. Image-to-image on the gateway is multipart `POST /images/edits` with the reference in a field literally named `image`; the CLI already does exactly that — do not "optimise" the edits call away.
Drive the whole set from one batch plan, so a poem is one command and one manifest. Write `plan.json`:
```json
{
"out_dir": "public/generated/<poem-slug>",
"images": [
{"name": "hero", "role": "hero", "prompt": "..."},
{"name": "scene-1", "role": "beat", "ref": ["hero"], "prompt": "..."}
]
}
```
```bash
python scripts/ark_image.py batch --plan plan.json
```
`"ref": ["hero"]` means “use the already-generated `hero` image inside `out_dir` as the image-to-image reference”, so the hero comes first and the beats follow in poem order. `batch` also writes `prompts.json` (every request and response) and `contact-sheet.html` into `out_dir`.
Then:
1. Keep the delivered files under `public/generated/<poem-slug>/`, with `prompts.json` and `contact-sheet.html` beside them.
2. Inspect the contact sheet — all images together, before any page work.
3. Reject or repair images with style drift, accidental text/seals, wrong era, malformed focal subjects, or mismatched recurring characters. Re-run the offenders with `--role repair` (`lite`), or regenerate a failed beat while still passing the hero as `--ref`.
4. Keep the delivered files. Ark returns JPEG at `2560x1440`, roughly 250–500 KB each, already inside the web delivery budget, so no re-encode step is required; the gateway returns PNG at roughly 2 MB, so add lighter derivatives when the page needs them. The extension follows the format actually returned — a `.png` result is never written under a `.jpg` name, and the CLI prints a `note:` when it rewrites one — so take the real filenames from `prompts.json`, not from the path you typed.
Size note: `2560x1440` is the only 16:9 value both Ark tiers accept. Never pass a shorthand such as `"2K"` — it silently returns a non-16:9 frame. The gateway accepts custom sizes and even `auto`, but it validates nothing (nonsense sizes come back 200), so the skill enforces its own `min_edge_px` / `max_edge_px` band there rather than relying on the API to reject a bad size.
Do not integrate uninspected generated assets.
### 5. Generate the narration
The narration track is generated, not borrowed; use a user-supplied or project-provided track only when the user actually brought one. Two engines are selectable through `narration.engine` in the config, or `--engine` on any command:
- `edge-tts` — Microsoft edge-tts, runs locally, free, no key; native output mp3.
- `doubao-seed-tts` — Volcengine Doubao `seed-tts-2.0` over the Ark plan gateway, using the same Ark key step 4 already resolves; native output wav, which the script transcodes to mp3.
Full comparison and configuration walkthrough: [references/narration.md](references/narration.md).
Verify engines and credentials first. `probe` really speaks one short phrase on each configured engine, so it is not free; `--dry-run` reports and synthesizes nothing:
```bash
python scripts/narration.py probe
```
If the voice is not decided yet, do not guess from an id. Audition the catalog and listen:
```bash
python scripts/narration.py audition --text "君不见,黄河之水天上来,奔流到海不复回"
```
`audition` writes one clip per voice plus an `index.html` with an `<audio>` player for each (default `public/audio/audition/`). Have the user pick, then pin the id in `narration.engines.<engine>.voice` or pass `--voice`.
Write the narration script next to the page: `#` lines are comments, a blank line starts a new paragraph (longer pause), and every other line is one spoken segment — one verse line per line. Keep the poem verbatim.
```bash
python scripts/narration.py build \
--text-file narration.txt \
--out public/audio/<poem-slug>.mp3 \
--srt public/audio/<poem-slug>.srt
```
`build` writes the joined mp3, a timing sidecar (`public/audio/<poem-slug>.mp3.json`, with per-line `start` / `end` / `duration`), and the SRT when `--srt` is given. Point `poem.audioSrc` at the mp3 in the next step.
### 6. Implement the webpage
Read [references/page-pattern.md](references/page-pattern.md). Prefer the bundled files in `assets/page-template/` as the starting point when the project has no stronger existing pattern.
Required page anatomy:
1. Hero: title, author/era, a defining line, a short invitation, and a scroll cue.
2. One full-viewport section per visual beat.
3. Each section: original line(s), literal meaning, close reading, and one compact visual device such as keywords, contrast, verb rhythm, emotional scale, or timeline.
4. Closing interpretation: genre/tune, structure, imagery, techniques, emotional arc, and central meaning.
5. Navigation between poetry pages when more than one exists.
6. A narration control. Generate the track with `scripts/narration.py` (step 5) unless the user supplied their own audio, then point `poem.audioSrc` at the produced mp3. Attempt autoplay, and always keep an accessible play/pause switch because browsers commonly block audible autoplay.
Use a fixed image stage with two overlapping texture planes or image layers. Crossfade when the active section changes; add only subtle zoom or pointer parallax. The image is the protagonist—keep cards translucent, readable, and smaller than half the viewport on desktop.
Do not use paid video generation unless the user explicitly asks. A high-quality image sequence with crossfades is the default.
### 7. Responsive and accessible behavior
- Use semantic headings, sections, buttons, and navigation.
- Keep the full focal subject visible on phones; prefer `contain` with a blurred full-bleed backdrop when a crop would destroy meaning.
- Support `prefers-reduced-motion` by disabling smooth scrolling, parallax, and long fades.
- Maintain readable contrast without dimming the artwork into obscurity.
- Do not load every full-resolution master up front; serve lighter derivatives where the page needs them.
- Give the narration button a visible text label, `aria-pressed`, keyboard focus styling, and a local error state. Do not hide the control merely because autoplay failed.
### 8. Verify the result
Run the production build. Then use a real browser to inspect:
- hero after assets finish loading;
- at least two middle sections after the crossfade settles;
- closing interpretation;
- one mobile viewport;
- narration source availability, autoplay fallback, and play/pause state;
- console warnings/errors.
The page fails QA if the generated images are not visibly switching, cards cover the main subjects, text is clipped, the page remains on a loading screen, or the imagery feels unrelated to the poem.
### 9. Deliver
Report:
- the local/page URL;
- source files changed;
- image asset directory;
- contact sheet path;
- narration asset URL or path, together with the engine and voice actually used;
- build and browser-QA result;
- the image tiers, providers and model ids actually used (for example `pro` on `ark` and `gpt-2` on `gpt-image`);
- the config file path (`config/ark.config.json`);
- the prompt-manifest path (`public/generated/<poem-slug>/prompts.json`).
Keep the dev server running when the user wants to review locally.
### 10. Voiceover video (optional, on request)
When the user asks to turn the page's images and narration into a 口播视频
(narrated video), read [references/video.md](references/video.md) and run
`scripts/video.py`. The video reuses what steps 4–6 already produced — scene
images, the narration mp3 and its per-line timing sidecar, and the page's design
tokens — and pins them onto a Remotion timeline (1920×1080, 30fps): one shot per
literary beat, hard-cut subtitles in two roles (`verse` for the original text,
`prose` for interpretation), a title card, an end card, film grain and vignette.
**Motion is not decoration here, it is the difference between a video and a
slideshow.** Four layers carry it, and a film that skips them reads flat no
matter how good the images are:
1. **Camera** — 13 moves, all with a baseline `scale ≥ 1.15` so there is real
travel room (±7%, not the ±1.4% that made earlier cuts invisible). Each move
is **two-phase**: it covers 90% of its travel in the first ~66% of the shot,
then barely creeps — so every shot *arrives* and then breathes, instead of
sliding uniformly to a stop. Plus a 3.4px handheld sway.
2. **Inside the frame** — deterministic dust/snow/ember particles in two depth
layers, drifting mist, a one-time light sweep, and a ±2.2% brightness
breathe. This is what makes a still photograph feel alive; camera motion
alone does not.
3. **Between shots** — five transitions, not one: `crossfade`, `dip-black`,
`flash-warm`, `push-through`, `light-leak`. Assign them along the poem's
emotional arc (a dip-to-black where the feeling breaks, a warm flash on the
declarative line), never as an even round-robin.
4. **Text** — the `verse` layer lights up **character by character in time with
the voice**, with the not-yet-spoken remainder held at 42% opacity; captions
develop in from blur; the title card converges its tracking and grows its
rule.
Write a `video-plan.json` (the storyboard release) — per-shot `move`,
`transition`, `fx` — then `probe` → `init` → `build` → `still` → `render` →
`frames`, reviewing stills and re-extracted frames before delivering. Full
command surface, plan schema, move/transition/fx vocabulary, and the QA
checklist of known failure modes: [references/video.md](references/video.md).
## Configuration
`config/ark.config.json` is the single place to change the image providers, the models, the role routing, the sizes, and the timeouts. Do not hard-code those values anywhere else.
Image settings live under `image`:
- `image.provider` — the default provider (`ark` in the shipped file). It applies to any tier that does not name its own provider.
- `image.providers.<name>` — one block per provider: `ark` (kind `ark`, Volcengine Ark 火山引擎方舟 Doubao Seedream) and `gpt-image` (kind `openai-images`, the third-party GPT-Image gateway). Each block carries its own `base_url`, endpoint paths, key settings, timeouts and limits.
Provider-level knobs, all per provider: `timeout_seconds`, `max_retries`, `retry_backoff_seconds`, `reference_transport` (`inline` for Ark, `multipart` for the gateway), `reference_max_edge` (bounds the size of a reference before an upload), `user_agent` (mandatory on the gateway, which sits behind Cloudflare), and `limits` (Ark's area band versus the gateway's edge band).
`models` maps a tier name to `{provider, id}` — `pro` and `lite` on `ark`, `gpt-2` and `gpt-2.5` on `gpt-image` — and a tier may still be written as a bare model-id string, which then uses the default provider. A config written entirely in the old shape (a top-level `api` block, plus a top-level `limits`, with no `image` block) is still read as a single provider, so nobody has to migrate anything to keep working.
Select a provider with `--provider <name>` on `t2i`, `i2i` or `batch`; in a batch plan an individual entry may carry its own `"provider"`, `"tier"` or `"model"`, which is how one poem mixes providers image by image. `probe` takes no `--provider`: it follows the tier→provider mapping in `models`, or `--tier`.
Keys never live in that file — it is committed to a public repository. Each provider resolves its own key, in this order:
1. that provider's environment variable — `ARK_API_KEY` for `ark`, `GPT_IMAGE_API_KEY` for `gpt-image`;
2. that provider's local override file — `config/ark.local.json` or `config/gpt-image.local.json`, both git-ignored;
3. that provider's `api_key` field in `ark.config.json`, for throwaway local experiments only.
Local override files, one per provider. `config/ark.local.json`:
```json
{ "api_key": "ark-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" }
```
`config/gpt-image.local.json`:
```json
{ "api_key": "lupi_xxxxxxxxxxxxxxxxxxxx" }
```
See [references/ark-api.md](references/ark-api.md) for the full parameter reference, the provider comparison, the verified size limits, error handling, and the config-file walkthrough.
`scripts/extract_imagegen_results.py` is a legacy fallback only, for environments still running Codex's built-in ImageGen; the Ark path above does not need it.
### Narration
The same `config/ark.config.json` carries the narration settings, under `narration`:
- `narration.engine` — the active engine: `edge-tts` (default; local, free, no key) or `doubao-seed-tts` (Doubao `seed-tts-2.0` through the same Ark key the images use). Change this one key to switch, or pass `--engine` per command.
- `narration.engines.edge-tts` — `voice`, `rate` (`"+0%"`; `-20%` reads slower), `volume`, `pitch` (`-8Hz` reads lower).
- `narration.engines.doubao-seed-tts` — `voice`, `speed` (integer percent delta, −50..100), `emotion` (empty = natural), `resource_id` (`seed-tts-2.0`), `url`, `sample_rate`.
- `narration.audio` — output `dir`, `format`, `bitrate`, `channels`, `sample_rate`, the `line_gap_seconds` / `paragraph_gap_seconds` pauses, and loudness normalisation.
- `narration.script` — `open_with_title`, `title_template`, `include_original`, `include_literal`, `include_analysis`: which text layers a structured `.json` narration source contributes. With a plain `.txt` script the file is spoken verbatim instead, and `--include` overrides the config for one run.
- `narration.voice_catalog` — the selectable voices per engine, each `{id, gloss}`; `gloss` is a gloss of the romanised id, not an official description.
- `narration.known_rejected` — voice ids that failed the engine's resource-id check, with the reason.
Change the voice in `narration.engines.<engine>.voice` or with `--voice <id>`, and do not pick one by reading its id — run `audition` and listen. Full command surface, catalogs, and troubleshooting: [references/narration.md](references/narration.md).
### Video
The same config file carries the video settings, under `video`: `fps` / `width` / `height`, `crossfade_frames`, `grain_opacity`, `tail_seconds`, `workspace`, `template_dir`, plus the motion knobs — `motion_gain` (global camera amplitude; 1 standard, 1.3 bolder, 0.6 more restrained), `handheld_px` (sway amplitude, 0 disables), `caption_reveal` (whether the `verse` layer lights up per character), `default_transition` — and two environment-specific knobs: `node_modules` (point it at any directory that already has `remotion@4` installed and `init` copies it offline instead of running `npm install`) and `browser_executable` (left empty, the CLI auto-detects a local Chrome/Edge, which Remotion needs to render). Details and the plan-file schema: [references/video.md](references/video.md).
## Bundled resources
- [references/poetry-analysis.md](references/poetry-analysis.md): textual segmentation and explanation rules.
- [references/image-direction.md](references/image-direction.md): visual bible, Seedream-shaped prompt patterns, and the reference-image chain.
- [references/ark-api.md](references/ark-api.md): the image provider reference — Ark Doubao Seedream and the GPT-Image gateway side by side, provider/tier selection, request/response schemas, verified size rules, error codes, config walkthrough, and CLI reference.
- [references/page-pattern.md](references/page-pattern.md): page architecture and QA details.
- [references/narration.md](references/narration.md): narration engines, CLI reference, script format, timing sidecar, voice catalogs, config walkthrough, and troubleshooting.
- `assets/page-template/`: generic HTML, TypeScript, CSS, and config starter.
- `config/ark.config.json`: image providers, models, routing, sizes, retries, and timeouts; `config/ark.local.json` and `config/gpt-image.local.json` each hold one local key only.
- `scripts/ark_image.py`: the image CLI for both providers — Ark and the GPT-Image gateway — with `probe` (add `--live` to prove gateway model ids), `t2i`, `i2i`, `batch`.
- `scripts/narration.py`: the narration CLI — `voices`, `probe`, `audition`, `say`, `build`, with `edge-tts` and `doubao-seed-tts` engines.
- `scripts/video.py`: the voiceover-video CLI — `probe`, `init`, `build`, `render`, `still`, `frames`, `run`, driving the bundled Remotion template from a `video-plan.json`. `init` copies the whole template `src/` tree (so new components ship automatically) and never overwrites an already-built timeline.
- `assets/video-template/`: the Remotion project. `lib/motion.ts` (13 moves, two-phase travel, handheld, safety clamp), `lib/fx.tsx` (motes / haze / light sweep / breathe — CSS-only, no SVG filters), `lib/transitions.tsx` (five transitions), `lib/scene-stage.tsx`, `lib/captions.tsx` (per-character reveal), `lib/cards.tsx`, `lib/atmosphere.tsx` (grain + vignette).
- `scripts/extract_imagegen_results.py`: legacy fallback for environments still on Codex's built-in ImageGen.
Files in this skill
- README.md
- SKILL.md
- agents/openai.yaml
- assets/page-template/poem-config.ts
- assets/page-template/poetry-page.css
- assets/page-template/poetry-page.html
- assets/page-template/poetry-page.ts
- assets/video-template/package.json
- assets/video-template/remotion.config.ts
- assets/video-template/src/PoemVideo.tsx
- assets/video-template/src/Root.tsx
- assets/video-template/src/index.ts
- assets/video-template/src/lib/atmosphere.tsx
- assets/video-template/src/lib/captions.tsx
- assets/video-template/src/lib/cards.tsx
- assets/video-template/src/lib/fx.tsx
- assets/video-template/src/lib/motion.ts
- assets/video-template/src/lib/scene-stage.tsx
- assets/video-template/src/lib/transitions.tsx
- assets/video-template/src/timeline.generated.ts
Attribution
Comments
Loading comments…
Newsletter
Get the weekly list of safe new skills, and the ones we pulled.