← All experiments

Reed

Reed is an offline-first narrative design and production workspace I created while writing Acqui, my side-project game set on Nazi-occupied Kefalonia in 1943. It began as a better way to move branching dialogue into Unity, and grew into the place where the narrative itself is written, tested, localized, reviewed, versioned, prepared for performers, and handed to the rest of the game.

Client Acqui · self-initiated
Role Product Design & Engineering
Services
Product DesignDesign EngineeringNarrative ToolingCross-Platform Development
Platforms macOS · Windows · Android / Daylight DC-1
Year 2026

Why I built it

Acqui follows Gerasimos, a thirteen-year-old Greek village boy and violinist, and Acqui, a sentient Italian robot named after the real Acqui Division destroyed on Kefalonia.

The story moves between Greek villagers, Italian soldiers, and German occupiers. It branches through authored player choices, but those choices are about mercy rather than winning. The dialogue moves between English, Greek, Griko, Italian, and German because the occupation did.

My first problem was practical: I wanted to write those conversations as conversations. Not as spreadsheet rows that only became meaningful after opening Unity, and not as a diagram that forgot everything production would need later.

A line needed a permanent identity. A choice needed a condition. A character needed context for the writer, translator, actor, and director. A translation needed to remain attached to its source after the graph changed. A rewritten recording needed to become a visible pickup rather than a filename someone had to remember.

The existing workflow divided those responsibilities between a Unity inspector, spreadsheets, writing documents, flowcharts, export scripts, and version control. That fragmentation was manageable for one scene and increasingly fragile across a game.

I wanted one place where the narrative could survive the entire journey from first draft to game build.

How Reed evolved

The original brief was narrow: build a visual branching-dialogue editor for Acqui and export its data to Pixel Crushers Dialogue System.

Using the application changed the brief.

Once I could write the graph, I needed to read it without the canvas. Once I could read it, I needed to play its conditions. Once the branches worked, I needed to see missing translations. Once the translations existed, I needed cast context and voice direction. Once people and agents could edit the same project, I needed history, conflict protection, backups, and review.

The product expanded around the lifecycle of a line:

write → connect → playtest → revise → translate → review → record → export → maintain

Pixel Crushers remains an important destination for Acqui, but Reed is no longer defined by a single Unity plugin. It now exports to common game and localization formats while keeping its complete branching graph available as deterministic project data.

The Reed graph workspace showing a multilingual conversation with distinct speakers, choices, conditions, and the line inspector open

The graph is the working document

Conversations flow through Line, Choice, and Junction nodes.

Lines contain spoken dialogue. Choices represent player-facing decisions and can keep menu wording separate from the words a character actually says. Junctions bring multiple paths back together without inventing empty dialogue.

Dragging from a connector to another node creates a relationship. Dropping that connector onto empty space asks what should be created there, turning branching into one continuous action.

Nodes move live with the pointer. Multiple nodes can be selected and repositioned together. The canvas supports mouse, trackpad, touch, keyboard, and pen without making the same gesture start two modes at once.

Speaker accents help reveal the rhythm of a conversation, while names, labels, icons, conditions, and variable references ensure the graph never relies on colour alone.

Around the canvas, Reed organizes the game into projects, levels, scenes, beats, and conversations. The graph shows moment-to-moment structure; the Beats workspace pulls back to pacing and dramatic shape.

The Reed beat map showing the selected scene and its conversation

Read and play it before the game sees it

A narrative tool should let the writing fail early.

Reed’s Playtest workspace walks through a conversation as a player would. It evaluates local variables and conditions, presents available choices, and preserves the route taken through the scene.

The Read workspace removes the graph entirely and presents the selected path as a clean table read with speakers, dialogue, and permanent Line IDs.

Search and validation work across the whole project. Reed can surface unreachable nodes, dead ends, missing translations, undeclared variables, unknown speakers, stale drafts, and ID conflicts before they become problems in a build or recording session.

Review snapshots provide a stable point of comparison for narrative milestones instead of treating every autosave as an approval.

The Reed text-only playtest with condition controls beside the active conversation
The Reed light-theme table-read view showing the selected path and permanent Line IDs

Localization belongs inside the story

Acqui needed five languages from the beginning, but the underlying problem was broader than Acqui.

Reed now includes 47 ready-to-use game language definitions, a game-market preset, regional variants, right-to-left language identity, and support for custom BCP 47 language tags.

Any two configured languages can be displayed together while writing. The translation workspace preserves the Line ID, speaker, scene, choice context, source text, translated text, and verification state instead of flattening the work into disconnected cells.

Spoken dialogue and player-facing choice text remain independently localizable. Greek, Griko, Italian, German, English, Arabic, Hebrew, CJK scripts, Cyrillic, and accented Latin text stay Unicode throughout the editor and exports.

For Acqui, localization is part of the characterization and history. For Reed, it became part of the data model.

The Reed translation matrix showing English, Greek, Italian, and German columns from a five-language project

Context travels with the line

A line is more than its text.

Reed keeps its speaker, listener, conditions, scripts, tags, text status, voice status, pronunciation, performance direction, story context, and stable identity together.

Characters can carry descriptions, localized names, actor notes, accent guidance, and voice direction. Variables have declared types, initial values, descriptions, and visible usage counts.

This means a translator can understand why a line exists, an actor can understand how it should be performed, and a programmer can understand what state controls it without reconstructing context from several documents.

If recorded dialogue changes, Reed automatically marks it for rework. The project does not rely on someone remembering that a finished audio file is no longer current.

The Reed characters workspace with production metadata for a speaking cast member and the player character
The Reed variable registry showing typed condition state and graph usage counts

Handoff for games, translators, actors, and studios

Reed’s export workspace grew from a single Unity handoff into several distinct production paths.

It can prepare:

  • a complete deterministic project snapshot for custom runtimes and readable Git review;
  • Unity Localization, Godot, Unreal String Table, Yarn Spinner, gettext, XLIFF, JSON, and CSV outputs;
  • a deeper Pixel Crushers Dialogue Database export for Acqui and compatible Unity projects;
  • readable screenplay Markdown;
  • studio and director PDFs with context, direction, Line IDs, and exact audio filenames;
  • focused actor packets with nearby dialogue and pickup space;
  • XLSX recording trackers and UTF-8 CSV manifests for studio and audio workflows.

The engine adapters do not pretend every narrative runtime behaves the same way. Reed’s project snapshot remains the portable complete graph. Localization-oriented exports carry strings and context into the tools that already know how to consume them.

Everything is written locally. Nothing is uploaded as part of export.

The Reed validation workspace showing a clear project
The Reed export workspace showing game, localization, and voice-production handoffs

Version control in a writer's vocabulary

Reed autosaves completed edits into the local project database. Rolling backups and named snapshots protect the work before Git enters the picture.

Git is optional and disabled by default.

When collaboration is enabled, Reed can create or connect a repository, make a story checkpoint, fetch changes, push a branch, open a pull request, review it, and merge it from inside the application.

Because the project is deterministic data under Git, more than one person can work on the same story at once. A narrative designer can draft dialogue on one branch while a game designer wires conditions, variables, and pacing on another, and the two efforts can be reviewed and merged without either quietly overwriting the other. Permanent Line IDs and optimistic concurrency mean a merge reconciles the same lines rather than guessing at them.

That makes Reed workable for a distributed team. Everyone keeps a full local copy of the project, writes offline, and syncs only when they choose, so collaborators in different places and time zones stay in one story without a shared always-on server sitting in the middle of the writing.

The interface describes what those actions mean for the narrative instead of assuming every writer understands working trees, upstream branches, and detached heads.

Local writing never waits for GitHub, and Reed performs no background synchronization. A remote action only happens when someone explicitly asks for it.

The Reed History workspace showing an offline story branch, a local checkpoint, and GitHub switched off

From a Mac editor to a cross-platform writing tool

Reed began as a dense macOS production workspace. Building the native Android version forced the interaction model to become much more deliberate.

The target was the Daylight DC-1, a tablet built around a different idea of a screen. Instead of a backlit colour LCD, it uses a reflective, paper-like display that stays readable in direct sunlight, refreshes fast enough to feel responsive rather than like e-ink, and shows everything in grayscale. There is no blue light. At night it lifts a warm amber backlight instead. It runs an Android-based system, takes a Wacom EMR pen, and is designed to be calm and low-distraction, closer to a notebook than a feed.

It was not an arbitrary port target. It is the machine I actually write Acqui on. I can work outside for hours, and the device leans offline enough that writing on it stays writing rather than drifting into everything else. That is the same posture Reed takes, so building a version for it meant I could take the dialogue outside and keep going without the tool quietly needing a connection to be useful.

Grayscale is a different set of primitives

On the Mac, colour is a free channel. Reed leaned on it lightly, but speaker accents, status, and validation could always fall back on hue if they needed to. A black-and-white display takes that channel away. Nothing on the DC-1 can mean “the red one” or “the green tick”.

So every distinction had to be re-encoded in the primitives grayscale still has: shape, weight, size, label, icon, fill, contrast, and position. Speakers are told apart by name and marker rather than colour. Status reads through wording and iconography. A failed validation carries a label, not a tint. Emphasis comes from value and typographic weight instead of a brighter accent.

Designing for the DC-1 first is what pushed the whole product to never rely on colour alone, and the Mac version inherited it. The graph reads the same for anyone who cannot depend on hue either, on a monochrome panel or otherwise.

Pen, touch, and one gesture at a time

The DC-1 is also touched in more ways than a laptop. Across a single session it might take a Wacom pen, a finger, a trackpad, a mouse, and a hardware keyboard, sometimes within the same minute. A pen can hover before it lands and carries a barrel button. A finger cannot hover and is far less precise. A trackpad wants momentum; a mouse wants exact pixels. Each of those is a different set of assumptions about what a gesture means.

Most of the port work was making sure one gesture only ever started one thing. Early on, a pen barrel-button drag would begin a marquee selection and pan the canvas at the same time. Pinch zoom fought with dragging a node. Each case was two behaviours claiming the same input, and each had to resolve into a single predictable action: focal pinch to zoom, barrel button to select, hover to preview, drag to move, with collapsible panels so a small screen never buries the graph under its own chrome.

I found most of this by using it. A gesture that reads fine in a spec falls apart the moment you are holding the device outdoors with a pen in one hand and the wrong two modes fire at once.

The desktop application ships for macOS and Windows. Android is a native Kotlin and Jetpack Compose interface shaped around these constraints rather than a shrunken desktop app in a web view, and all three platforms preserve the same project rules.

Agents as another editor, not another source of truth

Reed can also be edited by external coding and writing agents through its CLI and MCP interfaces.

On desktop, the MCP server communicates through local process input and output rather than opening a network port. An agent reads and mutates the same project model as the interface, through the same version checks, history, validation rules, and status limits.

An agent cannot mark authored text Final. That remains a human decision.

On Android, Remote Agent Access is an explicit, temporary foreground session scoped to one project. It exposes guarded narrative tools, not Git, SQL, arbitrary files, shell access, or an embedded language model.

Reed does not contain an LLM client. The model and its account belong to the external agent; the project remains inside Reed.

Drafting with an agent, writing it myself

The agent surface is not an afterthought for me. It is part of how I actually work in Reed.

Because an external agent can drive the same guarded narrative tools through MCP, I can use Claude as a writing coworker: block out a conversation quickly, rough in the branches, and get a workable first pass into the graph. Then I go in and edit it, cut it, and write the lines properly. The agent gets me to a draft I can react to. The writing stays mine.

The same thing helps with language. Acqui moves through Italian and Griko, and I speak neither. I will work with narrative designers who do, and the final localization is theirs. But using Claude as a first-pass localization partner means I can hand those collaborators something workable to start from instead of an empty column, which is a much better place to begin the real work together.

That is the version of AI I wanted in the tool. Fast enough to draft with, bounded enough that it never becomes the author, and useful precisely because a person is always the one deciding what is finished.

How it came together

Reed was developed through a long-running human and AI pairing loop.

I set the product direction, made the design decisions, used the real application, and tested it on macOS and the Daylight DC-1. Codex worked as an implementation partner across the shared core, desktop editor, Android application, tests, packaging, Git integration, CLI, MCP surface, exports, and documentation.

The useful feedback was rarely abstract. It was a node that only moved after the pointer stopped. A panel that looked interactive but was not. A GitHub sign-in that completed in the browser but never returned to the application. A pen button that began marquee selection and panned the canvas at the same time. A mobile layout that let the graph cover the project navigator.

Each failure became a product rule or regression test.

That process took Reed through several stages:

  1. A strict offline product and data contract.
  2. A versioned SQLite core with permanent Line IDs.
  3. A macOS graph editor for Acqui.
  4. Playtest, beats, translation, cast, variables, validation, and review.
  5. Voice-production and performer-facing exports.
  6. Autosave, backups, Git, GitHub, and pull requests.
  7. Native Android support for the Daylight DC-1.
  8. Local CLI and guarded MCP access.
  9. Windows packaging, broader language configuration, and common game-export formats.
  10. CI, documentation, release packaging, and open-source preparation.

The evolution did not come from adding features to make the list longer. It came from following the narrative further through production.

How it is built

The desktop application uses Electron, React, and React Flow over a shared TypeScript project core.

The Android application uses Kotlin and Jetpack Compose with an Android core implementation tested against the same project snapshots and mutation contracts. Android uses embedded JGit because it cannot depend on a child-process Git installation.

SQLite in WAL mode is canonical. Deterministic JSON is an export, not a second database. Every mutation increments the project version and records history. Optimistic concurrency prevents a stale interface, CLI command, or agent request from silently overwriting newer work.

Permanent Line IDs survive rewrites, graph changes, translation, export, voice recording, and engine handoff.

Offline behaviour is tested as a release contract. Opening Reed, opening a project, writing, saving, backing up, validating, playtesting, and exporting must not start a network process.

Reflection

The small interactions are the product.

“Build a node editor” sounds like a canvas problem. In practice, it is a contract between story structure, production metadata, and every input method used to manipulate them. If moving a node feels unreliable, the writer stops trusting the graph. If an ID changes, the studio stops trusting the export. If the application quietly needs a network connection, the project stops feeling owned.

Offline-first was most useful when it became structural rather than promotional. Keeping SQLite canonical and treating Git, GitHub, and remote agents as optional clients removed a class of loading, synchronization, and ownership ambiguity.

The biggest change was in Acqui itself. The graph stopped being a picture made before implementation. It became the source through which the same line moves from draft to branch, translation, playtest, review, performance, audio filename, and game data without losing its identity.

Reed began as tooling for one game. Following that game’s narrative through production turned it into a product for other narrative designers and independent developers facing the same fragmentation.

Current status

Reed is pre-1.0 and being prepared for open source.

Working builds exist for macOS, Windows, and Android. The graph editor, project storage, autosave, backups, history, localization, playtest, validation, voice workflow, exports, Git collaboration, pull requests, CLI, MCP integration, and offline acceptance checks are implemented.

Before a full public launch, the project still needs an open-source license decision, production code signing, macOS notarization, Android release signing, and certification against real fixtures from the exact engine and plugin versions it claims to support.

Results

3 platforms
macOS, Windows, and native Android, including the Daylight DC-1
47 language presets
plus custom BCP 47 language support beyond the built-in catalog
9 export formats
game and localization, plus screenplay, studio, actor, and audio manifests
Offline by default
no account, hosted store, subscription, telemetry, or required connection
← All work
Next
Daybreak →
View case →