Skip to content
Development
Skill

/visual-novel

Build a visual novel: a branching script, character and background display, a text box with choices, save/load, backlog, and skip/auto. Use for a VN, dating sim, or branching story game.

From plugin
awesome-gamedev-agent-skills
6.3k77 skills

Claude Code

$ npx -y skills add gamedev-skills/awesome-gamedev-agent-skills --skill visual-novel --agent claude-code

SKILL.md

visual-novel.SKILL.md
name: visual-novel
description: >
  Build a visual novel: a branching script, character and background display, a text box with choices,
  save/load, backlog, and skip/auto. Use for a VN, dating sim, or branching story game.

Visual Novel

A playbook for visual novels — the branching script, the presentation (text box, characters, backgrounds), choices, and the quality-of-life systems players expect (save anywhere, backlog, skip, auto). This is a **compositional** skill: it drives a dialogue engine and a UI layer. It does not re-teach the dialogue engine or UI nodes; it defines the script model and the player conveniences that make a VN pleasant to read.

When to use

  • Use when the game is **mostly reading branching text** with character art and backgrounds:

visual novel, dating sim, branching interactive fiction, story-choice game.

  • Use when designing a choice/route structure, story flags, or VN conveniences (backlog,

skip, auto-advance, save-anywhere).

**When *not* to use:** dialogue as one feature inside a larger game → `rpg` consuming `dialogue-systems`. Card/board play → other genres. For the branching-script engine itself, use `dialogue-systems` (Ink / Yarn Spinner).

**If the project is Ren'Py** (current stable 8.5.3): the engine already ships save-anywhere, rollback, a history (backlog) screen, skip that stops at unseen text by default (`preferences.skip_unseen = False`), auto-forward and a text-speed preference. Use and restyle those through its screens and preferences; apply this skill's design guidance (routes, flags, reactivity) rather than rebuilding the systems in Patterns 2–3.

Core loop

**Read a line → advance → (at a branch) make a choice → the story branches on flags/choices → read on → reach an ending.** The "game" is the *shape of the branching* and whether choices feel consequential; everything else is presentation and convenience.

Must-have systems

1. **Branching script** — ordered lines + choices + jumps, with conditions and variables (Ink/Yarn). 2. **Text box** — speaker name, body text, typewriter reveal, advance on click/key. 3. **Characters** — sprites with expressions/poses, positions, show/hide transitions. 4. **Backgrounds + transitions** — scene images, fades/dissolves. 5. **Choices** — present options, gate some on flags, record the pick. 6. **Story state** — flags/variables that branch the script and unlock content. 7. **Save/load (save-anywhere)** — full script position + state; multiple slots; quick save. 8. **VN conveniences** — backlog/history, skip (read text), auto-advance, text-speed setting. 9. **Audio** — music per scene, SFX, optional voice clips.

Design knobs

| Knob | Effect | Notes | |------|--------|-------| | Text speed / instant | reading comfort | Always allow instant + a skip. | | Auto-advance delay | hands-free reading | Tunable; pause on choices. | | Skip scope | re-reading | Skip *read* text only by default. | | Branch breadth/depth | replay value vs. cost | Branches multiply writing/art work. | | Flag-gated content | reactivity | Lines/choices that check past decisions. | | Route structure | story shape | Branch-and-merge vs. distinct routes (refs). | | Choice visibility | fairness | Show locked choices vs. hide them. | | Backlog length | convenience | Keep enough to re-read recent context. |

Patterns

1. Script as data the engine walks

# Pseudocode. Lines, choices, and jumps as data — usually authored in Ink/Yarn and stepped
# through by that runtime. The engine asks the script for "the next thing to show".
node = script.current()
if node.kind == "line":
    show_text(node.speaker, node.text)        # wait for advance input
elif node.kind == "choice":
    options = [o for o in node.options if condition_met(o.condition, flags)]  # gate by flags
    show_choices(options)                      # wait for selection
elif node.kind == "set":
    flags[node.var] = eval_expr(node.expr, flags)
script.advance(selected_option_or_none)

2. Typewriter reveal + advance (skippable)

# Pseudocode. Lay out the whole line once, then reveal it; a press first completes the line.
def show_text(speaker, text):
    name_label.text = speaker
    body_label.text = text                     # full text set up front: wrapping is decided now
    shown = 0.0
    while shown < visible_length(body_label):  # count displayed characters, not markup tags
        if advance_pressed():                   # first press: reveal the whole line instantly
            break
        shown += chars_per_second * frame_dt()
        body_label.visible_characters = int(shown)
        wait_one_frame()
    show_all_characters(body_label)
    backlog.append((speaker, text))             # once, when the line is complete
    wait_for_advance()                          # second press: go to the next line

Reveal with the label's visible-character count, not by slicing the string: slicing cuts markup tags in half and re-wraps the text every frame, so a word starts on one line and jumps to the next. Godot 4.7: `RichTextLabel.visible_characters` (or `visible_ratio`) with `visible_characters_behavior = TextServer.VC_CHARS_AFTER_SHAPING`; the default `VC_CHARS_BEFORE_SHAPING` leaves hidden text out of line breaking, so words still jump. Unity 6.3: `TMP_Text.maxVisibleCharacters` (TextMeshPro, part of the uGUI package).

3. Choice sets a flag that branches later content

# Pseudocode. Choices write flags; later conditions read them — that is "reactivity".
def on_choice(option):
    if option.set: flags[option.set] = True     # e.g. flags["helped_npc"] = True
    script.jump(option.target)                   # follow the branch

# Elsewhere, a line/choice/ending checks the flag:
if flags.get("helped_npc"): play_route("good_ending") else: play_route("neutral_ending")

Pitfalls / failure modes

  • **Save that only stores a checkpoint** → VNs need **save-anywhere**. Persist the exact script

position *and*

Read more
@gamedev-skills
Ships withawesome-gamedev-agent-skills

<img src="docs/assets/awesome-gamedev-agent-skills-banner.png" width="820" alt="awesome-gamedev-agent-skills — game-dev skills for AI coding agents.

Stars on GitHub

Momentum

stars in total
6.3k
Star history of gamedev-skills/awesome-gamedev-agent-skills on GitHub, a chart by star-history.com

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills10d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc12d293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react1d252k
@facebook@facebookInstall Skill

Questions about visual-novel.

Which coding agents can use visual-novel?
visual-novel installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.