EngineGodot 4.2 or newer
LanguageGDScript
DrivesA CanvasLayer/Control dialogue box
Time to run~4 minutes
LevelIntermediate
OutputOne script + a short scene setup note
DependenciesNone. No plugins.

The prompt

Open a fresh chat at claude.ai/new, paste, and send.

$ Build me a Godot 4.2+ dialogue system as a single GDScript that reads conversations from JSON.

# data format
- Dialogue is a JSON array of nodes. Each node: { "id": string, "speaker": string, "text": string, "next": string_or_null, "choices": optional array of { "text": string, "next": string } }.
- Loaded from a user-supplied path or a passed-in parsed Dictionary/Array.

# scene it drives (describe, do not build the .tscn)
- Assume a CanvasLayer with: a Label/RichTextLabel for text, a Label for speaker, and a VBoxContainer for choice Buttons. Export NodePaths to them.

# behaviour
- start(dialogue, start_id) begins the conversation.
- Typewriter reveal: show the current line one character at a time on a @export chars_per_second (default 40).
- On advance input (ui_accept): if still typing, instantly finish the line; if finished, go to next.
- If a node has choices, show a Button per choice after the text finishes; picking one jumps to that choice's next.
- When next is null and there are no choices, emit a signal dialogue_finished and hide the box.
- Emit a signal line_shown(id) each line (handy for triggers).

# constraints
- Godot 4 API (JSON.parse_string, signals via signal keyword, await/timers for the typewriter).
- Guard against a missing id (log and finish, do not crash).
- No magic numbers.

# return format
- The GDScript, plus a short comment block listing the scene nodes and export paths needed.
- No prose before or after.
Open in Claude ▸ Run · 4 min

What this gets you

A dialogue system that scales past the first cutscene. Because the conversation is data, you can write a whole branching quest in a text file and never touch code again. The typewriter and choice buttons are handled, and signals let the rest of your game react to specific lines.

It drives a plain Control scene you own, so styling the box to match your game is just editing nodes, not fighting a plugin's built-in look.

Why it matters

Data-driven, not code-driven

Hard-coding dialogue as print statements or match blocks falls apart around line thirty. Keeping it in JSON means writers, translators and you can change words without risking logic, and the same engine plays every conversation in the game.

The typewriter needs an interrupt

Players hammer the advance button. A typewriter that ignores input until it finishes feels sluggish. The right behaviour is: first press completes the line instantly, second press advances. This prompt asks for exactly that.

Branching by id, not by nesting

Flat nodes referenced by id (a graph) handle loops, merges and skips that nested if/else dialogue cannot. It is the structure every serious dialogue tool uses under the hood.

What good output looks like

How to wire it up

  1. Save the output as dialogue.gd.
  2. Build the scene from the comment block: a CanvasLayer with a text label, a speaker label, and a VBoxContainer for choices. Attach the script and wire the export NodePaths.
  3. Write a conversation in a .json file matching the node format.
  4. Load it with JSON.parse_string(FileAccess.get_file_as_string(path)) and call start(data, "intro").
  5. Connect the dialogue_finished signal to whatever should happen after the conversation.

Common gotchas

Text appears all at once

The typewriter loop is not awaiting between characters. It should reveal characters over time (a timer or await), not set the full string in one frame.

Choices do nothing

The buttons are created but their pressed signal is not connected to a handler that sets the next id. Bind each button to its choice when you create it.

It crashes at the end of a branch

A node's next points to an id that does not exist, or is null without being handled. Treat null-and-no-choices as finished, and a missing id as a safe end.

Where to go next

  1. Variables. Support simple placeholders like {player_name} replaced at display time.
  2. Conditions. Let choices carry a condition (a flag) so options appear only when unlocked.
  3. Save integration. Store seen lines and flags via the save system.

Use it however you want

The prompt is CC0, paste it, modify it, ship a game with it. The output Claude gives you is yours. A link back to pixeldex.dev in your project's README helps the next solo dev find this stuff.