A schema-driven content authoring studio for Godot projects.
Bleepforge is the editor your [GlobalClass] resources have always wanted: a focused, opinionated UI for building game content - dialogues, quests, items, karma impacts, NPCs, factions, balloons, project-specific concept categories, and shaders - that round-trips losslessly to and from Godot's .tres files (or for shaders, the .gdshader files themselves). The Godot project stays the source of truth; Bleepforge gives you the human-friendly surface on top of it.
Currently bootstrapped against Flock of Bleeps, an in-development 2D adventure-platformer about a not-very-bright robot judged worthless and dumped in a landfill. Long-term direction is to be generic for any Godot project's content (see Roadmap).
Godot's inspector is great for one resource at a time. It is not great for:
- Walking dozens of
DialogSequencefiles to find the one that branches into the conversation you're editing. - Keeping
QuestIdreferences consistent acrossNpcData,Quest, and dialogue choices. - Visualizing branching dialogue as a graph instead of nested arrays inside arrays.
- Authoring icon / portrait / banner images and seeing where each one is used across the project.
- Sanity-checking that no
Choice.NextSequenceIdpoints at a deleted sequence. - Everything an authoring workflow needs that isn't directly part of the runtime.
Bleepforge is what you reach for when the project gets big enough that "open the inspector" becomes the bottleneck.
Thirteen distinct surfaces, each tailored to one slice of the work:
| Surface | What it is |
|---|---|
| Workbench | The app's overview / homepage (v0.2.4). Project header strip, stats grid per domain, recent activity feed, integrity strip. Bleepforge-only. |
| Game concept | The project's pitch / acts / faction roles. Bleepforge-only. Was the homepage pre-v0.2.4; now a regular surface. |
| Dialogues | Multi-folder graph view + list view. Drag nodes, draw edges, double-click edges to rename, per-edge dashed/curved/waypoint style. Per-folder layout persistence. |
| Quests | Objectives + rewards as discriminated unions, auto-managed flag strips, link-out to giver NPC. |
| Items | ItemData + QuestItemData polymorphism. Card/list views grouped by category. |
| Karma impacts | Per-faction karma deltas, range-clamped, free-form-id-keyed. |
| NPCs | Full authoring - portrait, dialogs, quest entries, loot tables, casual remarks (balloon refs). |
| Factions | The four enum-keyed factions with banners + descriptions. |
| Balloons | The "Hi there!" lines NPCs say when you walk up. Cards mimic in-game speech bubbles, type-speed animates on hover. |
| Game Codex | Bleepforge-only multi-category notebook with user-defined property schemas (Hazards, Locations, etc.). Never round-tripped to Godot - staging ground for concepts that don't yet have a hardcoded domain. |
| Assets | Browse every image in the project. "Used by N" reverse-lookup against .tres + .tscn. In-app editor: crop, tint, flip, auto-trim, ML / heuristic background removal, "Magic crop" subject detection. |
| Shaders | Browse every .gdshader in the project. CodeMirror editor with GDShader syntax + gutter diagnostics (parser + WebGL errors line-anchored) + live WebGL2 preview canvas: re-translates GDShader → GLSL ES on every edit, auto-generated uniform controls (sliders for hint_range, color pickers for source_color, AssetPickers for each sampler2D), per-sampler texture-unit allocation, helper-function support with auto-substituted built-ins, hint_screen_texture aliasing for post-process shaders, ping-pong framebuffers + hint_previous_frame for trails / iterative effects. Cards also carry a user-pickable pattern (10 hand-designed pixel-art tiling SVGs — scanlines / bars / grid / lattice / diagonal / waveform / rings / bricks / circuit / stars) so you can tell two shaders apart at a glance. |
| Help | Bleepforge-only documentation library (11 categories, 67 entries shipped — including a Release notes category with one entry per version). Dev-mode-gated authoring. |
- Two-way
.tressync. Save in Bleepforge → atomic write to the matching.tres. Save in Godot → live watcher reimports and refreshes any open editor. No "click reimport" button. - Boot-time reconcile. Every server start rebuilds the JSON cache from
.tresvia the ProjectIndex — a content-driven runtime index that classifies every.tresby what it is (script_class / Slug presence) and every.tscnwith aDbItemNameas a pickup, with no hardcoded folder conventions anywhere. Move files around inside your Godot project freely; Bleepforge finds them at next boot. If you edit Godot while Bleepforge is off, the next launch picks it up. No drift. - Live SSE. Four event channels (sync / saves / assets / shaders) drive auto-refresh, toast notifications, and the live save-activity feed in real time. Shaders participate in the full sync surface — catalog refresh, Saves tab activity feed, and toasts on external add/change/remove (with per-window suppression so your own save doesn't double-feedback).
- Diagnostics page. Six tabs - Integrity, Reconcile, Logs, Saves, Process, Watcher - with severity-aware unified header icon. The save activity feed is SSE-pushed and updates as you edit.
- App-wide search at
Ctrl+K- substring matching across every authored entity by id and display name. No fuzzy-typo-tolerance to keep results predictable. - Theming. Eight color themes (dark / light / red / amber / green / cyan / blue / magenta) plus tunable body font, UI scale, and letter spacing. Bundled into named "global themes" you can save and switch between.
- Desktop wrap (Electron). Run as a desktop app via
pnpm dev:desktop. Diagnostics / Help / Preferences open as chromeless popouts with cross-window state sync.
- Node.js ≥ 20 (the engines field enforces this)
- pnpm ≥ 10 (
npm install -g pnpm) - A Godot 4 / C# project to point at (currently shaped around the Flock of Bleeps schema)
git clone https://github.com/ymoraviadev-droid/bleepforge.git
cd bleepforge
pnpm installThe Electron postinstall downloads a ~150 MB Chromium binary on first install. If pnpm skips it (the v10 default for new packages), the root package.json already has pnpm.onlyBuiltDependencies: ["electron"] to permit it. If for any reason it still doesn't fire:
node node_modules/.pnpm/electron@*/node_modules/electron/install.jsCopy .env.example → .env and point GODOT_PROJECT_ROOT at your Godot project:
cp .env.example .env
$EDITOR .envGODOT_PROJECT_ROOT=/home/you/Godot/your-project
PORT=4000 # optional, default 4000
DATA_ROOT=data # optional, default ./data
ASSET_ROOT=/home/you # optional, defaults to $HOMEThe Godot project root is required for the server to read or write .tres files in sync mode (the only mode pre-v0.2.5). v0.2.5 introduced two more modes — notebook (standalone, no Godot connection) and import-once (snapshot a Godot tree into a notebook). For a sync-mode project the env var here is the bootstrap fallback; once the v0.2.5 migration runs, the canonical source is the active project record (see Multi-project below). Notebook projects ignore the env var entirely.
pnpm dev- Vite dev server on http://localhost:5173
- Express API on http://localhost:4000 (proxied via Vite at
/api/*)
pnpm dev:desktopSame dev servers + an Electron window. Maximized, no menu bar, hot reload preserved. Diagnostics / Help / Preferences each open as their own chromeless popout. Closing the main window closes any open popouts and quits the app.
Note: the Electron launcher hardcodes
http://localhost:5173. Kill any otherpnpm devrunning beforepnpm dev:desktopor the desktop window may load the wrong dev session. Override withVITE_DEV_URL=http://localhost:5174 pnpm dev:desktopif needed.
pnpm dist # both platforms: Linux AppImage + Windows NSIS installer
pnpm dist:linux # Linux AppImage only
pnpm dist:win # Windows NSIS installer only (cross-build from Linux supported)Linux produces electron/release/Bleepforge-<version>-x86_64.AppImage (~115MB, single file, no install). Double-click to run; user state persists at ~/.config/Bleepforge/projects/<slug>/. Run pnpm install:desktop once to register the app in the KDE / GNOME application menu with pre-cached thumbnails.
Windows produces electron/release/Bleepforge-Setup-<version>-x64.exe (~86MB), an NSIS installer with the full wizard (welcome → install location → install), per-user install (no admin prompt), desktop + start menu shortcuts, and a working uninstaller. Unsigned — first install shows one SmartScreen warning the user clicks through with More info → Run anyway. User state persists at %APPDATA%\Bleepforge\projects\<slug>\.
Cross-building Windows from Linux requires a dedicated 64-bit Wine prefix at ~/.wine64-bleepforge — electron-builder's rcedit step (sets .exe metadata + embeds the icon) needs to operate on a 64-bit target, and a 32-bit Wine prefix can't manipulate it. First-time setup:
WINEPREFIX=~/.wine64-bleepforge WINEARCH=win64 \
WINEDLLOVERRIDES='mscoree=;mshtml=' wineboot --initpnpm dist:win defaults WINEPREFIX to that path automatically; override via env var if you want a different prefix.
The Help library ships inside both builds and seeds into userData on first launch when missing; on subsequent upgrades, only new help entries from the bundled seed are merged in — your edits to existing entries are never overwritten. Concept, Codex, and the .tres-derived domain JSONs are user-authored and start empty — point Bleepforge at your Godot project via the first-run welcome screen (or Preferences after first run), restart, and the cache rebuilds from .tres.
The build pipeline runs in four steps: client build (Vite) → server bundle (esbuild — workspace deps inlined, npm deps external) → electron main (tsc) → electron-builder (asar packaging + AppImage / NSIS assembly). Build output lives in electron/release/ and is gitignored (~400MB per build).
macOS (.dmg) is a config-only follow-up. Auto-update + code signing are deferred until distribution is something other than "the user runs the installer from disk."
.tres (Godot project) ⇄ Bleepforge server ⇄ React UI
↑ canonical (Express + watcher) (Vite)
└─── source of truth ─────────┘
│
└──► JSON cache in projects/<slug>/data/<domain>/
(rebuilt on boot, kept live by chokidar,
pushed back to .tres on every save —
only in sync-mode projects)
.tresis canonical;projects/<slug>/data/<domain>/<id>.jsonis a derived cache, rebuilt on every server start and kept in sync afterward. The cache JSONs are.gitignored (machine-local, regenerated on boot) — never hand-edited; drift gets reconciled away on the next boot.- Bleepforge-only authored state IS tracked in git:
concept.json, per-folderdialogs/<folder>/_layout.json, the entirecodex/tree, the entirehelp/tree,shaders/_meta.json.preferences.json(theme + Godot project root) is gitignored as machine-specific, along withprojects.jsonandactive-project.jsonwhich carry per-machine absolute paths. - Schemas live in
shared/src/, validated at the server boundary on read and write.
For the deep-dive reasoning behind every architectural choice, see CLAUDE.md. It's the project bible - schema definitions, design decisions, write-back internals, watcher behavior, theme system, the lot.
As of v0.2.5 a single Bleepforge install holds many projects, exactly one active at a time. The active-project chip in the sidebar (under the BLEEPFORGE wordmark) shows which one you're editing; click it to open /projects and switch. Three creation flows from the + New project button on that page:
- Notebook — standalone. Bleepforge owns the data + assets + shaders inside the project. No Godot connection.
- Sync to Godot — pick a Godot folder. Two-way live sync:
.tresfiles are canonical, Bleepforge mirrors them as JSON on every boot and writes edits back on save; the watcher catches Godot-side changes live. - Import once from Godot — snapshot a Godot tree into a fresh notebook.
.tresdata lands as JSON, referenced images + shaders get copied into the new project'scontent/dir, refs are rewritten to the portablecontent://URL scheme. After the snapshot the project is independent.
Switching projects hot-reloads the server in-process (~15ms) — no app restart required, no window flash. POST /api/projects/reload re-reads the active pointer + record and rebuilds the watcher, caches, and index against the new state. Works identically in dev and packaged modes. Rename + Delete live on each card's ⋯ menu; sync-mode projects' Godot trees are never touched by delete regardless of choice — Bleepforge only deletes what it owns.
The legacy data/ layout (pre-v0.2.5) migrates automatically on first boot: contents move to projects/<slug>/data/, slug derived from concept.json's Title, the Godot root pulled out of preferences.json into the new project record.
bleepforge/
├── shared/ TypeScript types + zod schemas (the contract)
├── server/ Express + TS - REST API, .tres parser/writer, watcher
├── client/ React + TS + Tailwind + Vite - every authoring UI
├── electron/ Electron main process + preload (desktop wrap)
├── godot-lib/ Godot companion library (v0.2.6+ — manifest emitter, runtime registry bases)
├── projects.json Multi-project registry (gitignored — absolute paths)
├── active-project.json Active-project pointer (gitignored)
└── projects/<slug>/ Per-project storage
├── data/ Derived JSON cache + Bleepforge-only state
│ ├── concept.json The project pitch (Bleepforge-only)
│ ├── preferences.json Active theme + Godot project root (gitignored)
│ ├── dialogs/<folder>/ Per-folder dialog cache + _layout.json
│ ├── balloons/<model>/ Per-NPC-model balloon cache
│ ├── codex/<category>/ Game Codex (Bleepforge-only, never round-trips)
│ ├── help/<category>/ Help library (Bleepforge-only)
│ ├── shaders/_meta.json Per-shader pattern + color
│ ├── quests/, items/, karma/, npcs/, factions/ Flat-domain caches
└── content/ Notebook-mode assets + shaders (not present in sync mode)
pnpm workspace; each package has its own tsconfig.json and shares strict TypeScript across the monorepo.
Run from the repo root:
| Command | What it does |
|---|---|
pnpm dev |
Start server (tsx watch) + client (vite) in parallel - browser workflow |
pnpm dev:desktop |
Same as pnpm dev plus the Electron window |
pnpm dist |
Build packaged binaries for both platforms — Linux AppImage + Windows NSIS installer (Vite + esbuild + tsc + electron-builder) |
pnpm build |
Build all workspaces (tsc for shared/server/electron, vite build for client) |
pnpm typecheck |
Run tsc --noEmit across all workspaces |
pnpm sync:from-userdata |
Pull AppImage edits back into the repo (~/.config/Bleepforge/projects/<slug>/data/ → ./projects/<slug>/data/). Walks every project on either side; dry-run + confirm by default; -y to skip prompt. |
pnpm sync:to-userdata |
Push repo content into the running AppImage's userData (opposite direction). |
Server-side dev tools (run via pnpm --filter @bleepforge/server <name>):
| Command | What it does |
|---|---|
harness |
Walk every .tres and confirm parser+emitter is byte-identical (round-trip test) |
canary <slug> |
Apply a JSON edit to one .tres and show the unified diff (Item) |
canary-karma <id> |
Same, for KarmaImpact |
canary-dialog <folder> <id> |
Same, for DialogSequence |
canary-quest <id> |
Same, for Quest |
migrate-subids |
One-shot migrator that adds _subId fields to existing JSON for reorder-safe writes |
- Frontend: React 19, TypeScript, Tailwind v4, Vite 6, @xyflow/react (graph view), Fuse.js (search), @imgly/background-removal (ML bg removal)
- Backend: Express 5, TypeScript, chokidar (watcher), zod (schema validation)
- Desktop: Electron 33 (main process + preload only - renderer is the same React app)
- Persistence: Godot's
.tresformat (canonical) + JSON cache (derived) - both committed to git - Conventions: ES modules end-to-end except Electron's main (CJS); strict TypeScript including
noUncheckedIndexedAccess+noImplicitOverride; pnpm workspaces
Done:
- All 12 authoring surfaces
- Two-way
.tressync (boot reconcile + live watcher + on-save writeback) - Diagnostics page (6 tabs)
- App-wide search
- Theming + global theme bundles + cross-window theme sync
- Assets gallery + image editor with ML / heuristic bg removal
- Game Codex (user-defined category schemas)
- Shaders surface with CodeMirror editor (+ GDShader syntax + gutter diagnostics) + WebGL2 live preview + GDShader → GLSL ES subset translator with multi-texture + helper-function support + sampler hints +
hint_screen_texture+ ping-pong framebuffers forhint_previous_frame(trails / iterative effects). Full sync parity with the.tresdomains — catalog refresh, Saves tab integration, cross-window toasts. - In-app Help library
- Electron desktop wrap (dev + Linux AppImage + Windows NSIS installer via
pnpm dist/pnpm dist:win) - First-run welcome flow for packaged builds (v0.2.3) — when no
preferences.jsonexists yet, the renderer shows a native folder picker instead of a broken-looking shell.
In progress (v0.2.6 → v0.3.0):
-
Genericize for any Godot project. Releases on this arc; three shipped, three remaining:
- v0.2.6 ✓ shipped — manifest contract + library tier 1 (abstract base classes for the four entry kinds:
domain/discriminatedFamily/foldered/enumKeyed) + editor reads manifest. Editor UI didn't change; FoB workflow stayed unchanged. Foundation. - v0.2.7 ✓ shipped — generic
.tresmapper (writer half — 12 field-type handler dispatch, override registry, sub-resource reconciliation, ext-resource minting, AtlasTexture preservation, scriptIndex module) + manifest-driven discovery end-to-end (manifestCache singleton, projectIndex extension for all 4 entry kinds,/api/manifest-domainendpoints, generic<DomainList>UI, dynamic sidebar nav, AppSearch indexing). Read-only MVP — Bleepforge edits any manifest project enough to see its declared domains + discovered entities. Karma byte-identical writeback validation (6/6 against real FoB corpus) proves the writer works; production dispatch waits for v0.2.9. - v0.2.8 ✓ shipped — generic
.tresimporter (reader half — 10 handler files covering all 12 field types via uniform dispatch, override registry for FoB's seven hand-rolled importers,_subIdpopulated on sub-resource array reads, AtlasTexture preservation contract). JSON cache for manifest-discovered domains (kind-aware paths,.gitignoremarkers per domain folder), boot reconcile extension, watcher reimport extension keeping the cache live via the same generic path.SyncEvent.domainwidened tostringso manifest names flow through the sync bus + saves audit feed + toast bridge. Round-trip harness (parse → import → write → emit, byte-identical) locks the contract: karma 6/6 architectural proof + faction 4/4 + balloon 2/2. FoB workflow unchanged; production dispatch + override retirement land in v0.2.9. - v0.2.9 ✓ shipped — generic edit UI foundation. Generic
<DomainEdit>page at/manifest/:domain/entity/*driven by manifest field declarations + per-field-type form renderers covering all 11 manifest field types (text / multiline / number / bool / enum / flag / ref / texture / scene / array / subresource) +writeTresGenericproduction wrapper wired into newGET / PUT / DELETE /api/manifest-domain/:domain/entity/*endpoints. Recursive array + subresource pair handles nested sub-resource structures (bordered card per item, ↑ ↓ × controls,_subIdpreserved through reorder). NooverrideUimechanism — architectural reframe locked 2026-05-17: Bleepforge can't know FoB exists post-v0.3.0, so bespoke surfaces graduate to first-class manifest-driven generic capabilities in v0.2.10+ instead of being preserved as registered overrides. Ships pure infrastructure: zero FoB-side changes, zero existing UI regressions, all v0.2.7 / v0.2.8 harnesses still clean. - v0.2.10 — IN PROGRESS (2026-05-19). FoB-side port done (ItemData / Quest / KarmaImpact / FactionData + 4 ItemData variants with
[BleepforgeVariantValue]all inheritingBleepforgeResource; lightweight new registries; project.godot autoloads updated). Godot-lib emitter now detectsdiscriminatedFamilyfrom variant attributes regardless of registry base. Bleepforge writer + importer + boot reconcile + watcher all retired their FoB-domain hardcoded paths for the four —FOB_DOMAINSshrunk to npc/dialog/balloon, override registries pruned, generic write/read pipeline carries item (including variant fields like Damage/AttackSpeed), quest, karma, faction. Legacy/items/:slugetc. URLs redirect to/manifest/<domain>/entity/<id>so users land on the new variant-aware UI. 1,798 lines of dead server code retired. End-to-end 2-way sync confirmed including variant fields. Pending for stable cut: deleting the legacyfeatures/{item,karma,faction}/client folders, dynamic-from-manifest sidebar/AppSearch/integrity (Phase 3), writer field-ordering polish, version bump + release notes + help library entries. Three domains (dialog, balloon, npc) remain on bespoke surfaces at v0.2.10 close — they carry heavier UX that graduates in v0.2.11+. Known Godot-side gotcha: after porting C# resource classes, Godot's editor caches a stale script binding (Godot bug #81002 family); Inspector edits to variant fields may silently fail to persist on save. Workaround: kick Godot's filesystem watcher with an external write (text-editor save /touch) — Godot reloads → script binding refreshes → Inspector saves work normally from that point on. - v0.2.11+ — bespoke surface graduations, one focused release per surface: graph view (DialogGraph generalized; manifest opts in via
view: "graph"), speech-bubble card variant for spoken-line domains, heavier inline sub-resource UX for complex domains. Each release turns a generic UI pattern into a first-class manifest-driven capability; the per-domainfeatures/folder dissolves into a manifest declaration + a contribution to the generic capability. "Features only ADD" — nothing in the current UI disappears, it relocates from per-domain bespoke code to manifest-driven generic infrastructure. - v0.3.0 — headline cut: "Bleepforge is a generic content studio for any 2D Godot 4 project, or works standalone in notebook mode." Polish, docs, the advertise-able release. Bleepforge core has zero project-specific code.
See the Genericization arc section in CLAUDE.md for the full design — manifest spec (4 entry kinds + 12 field types), locked decisions (monorepo, tiered library, no demo game), and the v0.2.7 + v0.2.8 generic mapper + importer architecture.
- v0.2.6 ✓ shipped — manifest contract + library tier 1 (abstract base classes for the four entry kinds:
Next:
- macOS packaging + auto-update + code signing. Deferred until distribution is something other than "the user runs the installer from disk." macOS (.dmg) is a config-only follow-up to the existing electron-builder pipeline. Windows NSIS shipped unsigned in v0.2.3.
Some opinions baked into Bleepforge worth knowing if you fork or contribute:
- Three persistence channels per save (memory state → localStorage cache → server) - the user always sees their last-applied state instantly even if the server save is in flight or fails.
- Schema mirrors the Godot resource fields 1:1. PascalCase keys, same field names. Pays off for manual transcription today, keeps
.tresparsing viable later. - Enums serialize as strings in JSON (
"QuestItem","CollectItem","Scavengers") - readable in diffs, robust to reordering - and as ints in.tres(Godot's choice). - Click on any image opens a preview, not the editor. Editing is always via right-click. One rule everywhere; no accidental modal opens.
- Defense in depth on path safety. The writer refuses any target outside the resolved Godot project root, every file-touching server endpoint validates via
path.relative + startsWith(".."), atomic writes (temp + rename) for both.tresand image saves. - Don't trust client-side state alone. Every PUT is validated server-side via the same zod schemas the client uses; structural compat between client and server is the contract enforced at the boundary.
Bleepforge is open source, with a deliberate split between library and editor:
godot-lib/(the Godot companion library that ships into your Godot project) — Apache License 2.0. Permissive: drop it in without thinking. See godot-lib/LICENSE.- The editor (
client/,server/,shared/,electron/, plus everything else in this repo) — GNU Affero General Public License v3 or later. Copyleft: forks must remain open, including SaaS hosts. See LICENSE.
Why the split: a permissive license on the library maximizes adoption (game devs drop it into their projects without worrying about license compatibility); a copyleft license on the editor prevents rebadge-and-monetize forks (any fork must also be AGPL, removing the financial incentive to take Bleepforge, slap a new name on it, and sell it). Apache 2.0 + AGPL v3 are both OSI-approved open-source licenses.
The names "Bleepforge" and "godot-lib for Bleepforge" and the Bleepforge logo are unregistered trademarks of Yehonatan Moravia. Forks are welcome under the licenses above; use a different name. See TRADEMARK.md for the full policy.
Premium tier-2 add-ons (DialogRunner, FlagStore, KarmaApplier, and similar opinionated runtime helpers) are deferred indefinitely. If they ever ship, they'll be separate proprietary products, not subject to AGPL — AGPL covers the editor itself, not separately-distributed add-ons you build alongside it.
Copyright © 2026 Yehonatan Moravia.
Authored by Yehonatan Moravia & Archie - ymoravia.dev@gmail.com
Bleepforge is built collaboratively. Documentation evolves alongside the code; CLAUDE.md and this README are the running record. PRs and issues welcome if you're working on a similarly-shaped Godot project and want to fork the editor toward your own schema.