# Paper To Storyboard

> Convert an academic PDF paper into a single-page scrollytelling website (index.html + style.css + script.js + extracted figures + optional AI cover) using a fixed dark/light scroll-snap chassis. Use when the user provides a PDF and asks for a "storyboard", "scrollytelling page", "paper-to-web", "narrative website", or "convert paper to webpage".

- **Type:** Skill
- **Install:** `agentstack add skill-maoransun-paper-to-storyboard-skill`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [MaoranSun](https://agentstack.voostack.com/s/maoransun)
- **Installs:** 0
- **Category:** [Content & Media](https://agentstack.voostack.com/c/content-and-media)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [MaoranSun](https://github.com/MaoranSun)
- **Source:** https://github.com/MaoranSun/paper-to-storyboard/tree/main/skill

## Install

```sh
agentstack add skill-maoransun-paper-to-storyboard-skill
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# paper-to-storyboard

Turn an academic paper PDF into a dark, scroll-snap, single-page website with the same chassis as the reference example. The chassis (HTML scaffold, CSS layout/animations, vanilla JS IntersectionObserver) is fixed. Only the color palette and per-section content change per paper.

> **Run this skill on a strong model (Opus).** The hard part isn't the scripts — it's the judgement: mapping a paper onto the 9-slot narrative arc, rewriting body copy to fit display type, picking layouts and a palette, and composing the cover concept. Weaker models (e.g. Sonnet) tend to produce flat narratives, mis-assigned layouts, and verbatim-dumped paragraphs. If you're not on Opus, tell the user to switch with `/model opus` before invoking.

## Inputs

- `pdf_path` (required, absolute path)
- `out_dir` (default: `./storyboard/`)
- `palette` (optional: `warm | cool | earth | clinical | tech`) — if omitted, **ask the user** via AskUserQuestion
- `mode` (optional: `dark | light`) — if omitted, **ask the user**
- `typography` (optional: `editorial | modern | tech | academic`) — if omitted, **ask the user**
- `generate_cover` (optional: `true | false`) — if omitted, **ask the user**
- `title_override`, `subtitle_override` (optional)

When any of `palette`, `mode`, `typography`, `generate_cover` is explicitly supplied by the user when they invoke the skill, skip the corresponding question and use the supplied value.

### Style options

- **palette** — color family. Auto-pick from topic keywords; explicit `palette` arg wins.
- **mode** — `dark` keeps the reference chassis (white-on-dark with animated gradient + noise). `light` flips text to near-black, bg to light tints of the palette, button colors, overlays, and animation opacities.
- **typography** — font pairing:
  - `editorial`: Playfair Display + Inter (default — newspaper/longform feel)
  - `modern`: Space Grotesk + Inter (clean, product-design)
  - `tech`: JetBrains Mono + Inter (engineering / lab notebook)
  - `academic`: Crimson Pro + Source Sans (journal / scholarly)

## Workflow

Execute these steps in order. The skill directory is `~/.claude/skills/paper-to-storyboard/`. Run each script with absolute paths.

### 1. Ingest PDF text

```
python3 ~/.claude/skills/paper-to-storyboard/scripts/extract_text.py  /content.json
```

Produces `content.json` with `{title, authors, affiliations, abstract, doi, keywords, sections[], figures_meta[], stats_candidates[], tables[]}`.

`tables[]` holds ruled tables pulled by pdfplumber, each tagged with `numeric_columns` and a `chart_ready` flag (true = it has at least one label column and one numeric column). These are **ground-truth values** — prefer them as the data source for a `chart` slot (see step 6). Extraction is best-effort: it catches ruled tables well, may miss borderless ones, and can merge multi-line headers — always eyeball the `rows` before charting.

### 2. Extract figures

```
python3 ~/.claude/skills/paper-to-storyboard/scripts/extract_figures.py  /figures/
```

Produces `figures/figure1.png` … `figureN.png` and `figures/figures.json` with captions, page numbers, bboxes.

### 3. Make figures transparent

For each figure that will appear in the page (typically all of them):

```
python3 ~/.claude/skills/paper-to-storyboard/scripts/make_transparent.py /figures/figureN.png /figureN.png
```

This writes transparent PNGs directly into `out_dir/` (alongside the templates), where the HTML references them. The default mode is corner flood-fill (good for plot figures with white backgrounds). For photos/schematics, pass `--mode rembg`. If `rembg` isn't installed, it falls back to the flood-fill mode with a warning.

### 4. Ask the user for style choices

Before building the storyboard, read enough of the PDF to draft a 1-sentence topic summary, then use **`AskUserQuestion`** to let the user pick the visual style. Show your recommendation as the first option in each list, label it `(Recommended)`, and explain *why* in its description.

Issue these questions in a **single `AskUserQuestion` call** (batch them — don't ask one at a time):

1. **Palette** — 4 options out of `warm`, `cool`, `earth`, `clinical`, `tech` (recommended first, then 3 sensible alternatives for this paper).
   - Keyword guide for the recommendation: heat/energy/fire → `warm`; water/climate/ocean → `cool`; biology/ecology/agriculture/plants → `earth`; medicine/clinical/health/disease → `clinical`; computing/AI/ML/robotics → `tech`. Urban / cities / sociology papers often fit `cool` or `earth`. Default `warm`.

2. **Mode** — 2 options: `dark`, `light`.
   - Default `dark` (matches the reference chassis identity, more cinematic). Recommend `light` only when the paper is text-heavy with few or busy figures where readability dominates.

3. **Typography** — 4 options: `editorial`, `modern`, `tech`, `academic`.
   - Keyword guide for the recommendation: longform/humanities/policy → `editorial`; product / startup / design → `modern`; CS / AI / engineering → `tech`; medicine / scholarly / journal → `academic`.

4. **Generate cover?** — 2 options: `Generate (~$0.04)`, `Skip`.
   - In the `Generate` option's description, show the 1-sentence cover concept you'd send to the image model so the user can preview what they'd be paying for.
   - Recommend `Generate` if `OPENAI_API_KEY` is set, otherwise `Skip`.

Use the user's answers in the steps below. If the user picks `Skip` for cover, omit step 5 and don't add `cover_image` to the title section.

### 5. (Optional) Generate the title cover image

Only if the user picked `Generate` in step 4. Compose a 1-sentence `--concept` describing the paper:

```
python3 ~/.claude/skills/paper-to-storyboard/scripts/generate_cover.py \
  --concept "" \
  --palette  \
  --mode  \
  --out /cover.png
```

Calls OpenAI `gpt-image-1` (~$0.04 medium, $0.25 high — defaults to medium). To preview the prompt without spending: add `--prompt-only`.

When `cover.png` exists in `out_dir` AND the title section in `storyboard.json` has `"cover_image": "cover.png"`, render.py automatically inlines it as the title-bg with a palette-tinted gradient overlay. If either is missing, the title slot falls back to the default gradient.

If `OPENAI_API_KEY` isn't set, generation will fail with a clear error — fall back to skipping the cover and proceed.

### 6. Map content to storyboard slots

Read `content.json` and `figures/figures.json`. Build a storyboard JSON object matching `schemas/storyboard.schema.json`. Save as `/storyboard.json`.

The schema has **9 fixed slots** (you may skip slot 6 if the paper has only one main result):

| Slot | Suggested layout(s) | Maps to |
|------|---------------------|---------|
| 0 — title | `title` | Paper title (rewritten as a hook), 1-line subtitle from abstract |
| 1 — hook | `split`, `quote` | First paragraph of Introduction ("why now") |
| 2 — problem | `split`, `split_reverse`, `quote` | The gap/limitation paragraph |
| 3 — method | `split`, `split_reverse`, `stacked` | One-line methods + schematic figure |
| 4 — keyFinding | `impact`, `impact_single`, `stats_grid`, `chart` | Headline number(s) |
| 5 — dataNarrative | `split`, `stacked` | Main results figure + body + insight callout |
| 6 — secondaryFinding | `split_reverse`, `comparison` | Temporal/subgroup result or A-vs-B contrast (skippable) |
| 7 — insight | `insight`, `quote` | Discussion takeaway, call-to-action or pull quote |
| 8 — credits | `credits` | Authors, affiliations, DOI link |

### Layout catalogue

- **`title`** — hero. Big heading + subtitle, optional `cover_image` background.
- **`split`** — text-left, figure-right (default workhorse).
- **`split_reverse`** — figure-left, text-right (alternate to break rhythm).
- **`split_no_image`** — text-only with optional `insight` callout.
- **`stacked`** — figure on top, text below. For wide/panoramic figures.
- **`fullbleed`** — figure fills the section as background, text overlays in a glass panel. **Avoid for paper figures** — most scientific charts don't read well at viewport scale and the glass panel competes with the data. Reserved for the rare atmospheric photo or hero rendering (not for plots/maps/diagrams from a paper).
- **`quote`** — large italic pull-quote with attribution. Lift a striking sentence verbatim.
- **`impact`** — two big numbers vs. each other.
- **`impact_single`** — one big number with a caption.
- **`stats_grid`** — 3–4 metric cards (`stat_items: [{number, label, accent}]`).
- **`chart`** — vanilla (no-CDN) bar chart that grows on scroll and shows exact values on hover/focus. Use it instead of `stats_grid` when 2–6 numbers form a *comparison the reader should see as bars* (e.g. an ablation, before/after, per-group precision). Spec lives in a `chart` object: `{type: "bar", unit, y_max, data_source, series: [{label, value, accent}]}`. **Only `bar` is supported** — line/scatter are intentionally excluded because their values can't be recovered reliably. **`data_source` is mandatory and load-bearing**: use `"table"` or `"text"` only when the numbers are ground-truth (a data table or values quoted in the prose); use `"estimated"` if you read them off a figure by eye — that renders an "approximate, not exact" caption so the reader isn't misled. Never fabricate values to fill a chart; if you can't source the numbers, use a non-chart layout.
- **`comparison`** — two/three side-by-side text columns (`columns: [{heading, body, accent}]`).
- **`insight`** — discussion takeaway with a call-to-action box.
- **`credits`** — authors + affiliations + DOI button.

`accent` modifiers (for `stats_grid` cards and `comparison` columns): `warm`, `cool`, `caution`, `extreme`. Empty = default.

Rules for filling slots:
- Rewrite paper prose into 1–3 short sentences per slot (paper prose is too dense for 1.2rem display type).
- Lift headline numbers and key definitions **verbatim** from the paper.
- For slot 4, the two big numbers should be a meaningful comparison (e.g., baseline vs treatment, outdoor vs indoor). If the paper has no natural comparison, use a single big number and one short descriptor.
- **Charts (slot 4 alt):** if `content.json` has a `chart_ready` table whose rows form a clean 2–6 bar comparison, prefer the `chart` layout over `stats_grid`. Read the actual `rows` (don't trust the merged header blindly), pick one label column + one numeric column, and emit a `chart` object with `data_source: "table"`. If the numbers come from prose instead, use `data_source: "text"`. **Only use `data_source: "estimated"` if you read values off a figure by eye — and prefer not to: a wrong value under a real citation is worse than no chart.** Never invent values.
- For each split-layout slot, pick a figure from `figures.json` by relevance (use the caption to judge). Same figure should not be reused across slots.
- If the paper has fewer than 4 figures, some split-layout slots become bare (drop the image_content div).

### 7. Emit the site

Copy the templates and substitute placeholders:

```
python3 ~/.claude/skills/paper-to-storyboard/scripts/render.py \
  --storyboard /storyboard.json \
  --palette  \
  [--mode dark|light] \
  [--typography editorial|modern|tech|academic] \
  --out 
```

This writes `index.html`, `style.css`, `script.js` to `out_dir`. The transparent figures (step 3) should already be in `out_dir`.

### 8. Preview

```
python3 ~/.claude/skills/paper-to-storyboard/scripts/preview.py 
```

Prints `http://localhost:8765/` and opens it on macOS.

## Reference

A fully filled `storyboard.json` example ships in this skill: `examples/reference_storyboard.json`. Additional rendered examples live under `examples/` in the repo (https://github.com/MaoranSun/paper-to-storyboard).

## Dependencies

```
pip install pdfplumber pymupdf pillow
# Optional, for background removal of complex figures:
pip install rembg
```

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [MaoranSun](https://github.com/MaoranSun)
- **Source:** [MaoranSun/paper-to-storyboard](https://github.com/MaoranSun/paper-to-storyboard)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-maoransun-paper-to-storyboard-skill
- Seller: https://agentstack.voostack.com/s/maoransun
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
