Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Blog of sorts to sort out my thoughts

See the navigation on the left.

Non-technical

You can listen to my music on Deezer or Spotify.

Storytelling Engine

Yeah, I don’t have a better name.

This is the preliminary outline for a Rust / Bevy based ‘engine’ which I hope will be suitable for handling basic visual novel stuff, but also be flexible enough to serve a wide variety of purposes and games, hypothetically even complex CRPG dialog trees and open world exploration type things.

My thesis right now is that writing the engine will be relatively easy (i.e. there will be no Hard or Cursed1 problems, but not that it’s going to be done in an afternoon), but making an editor which would be easy to learn, pleasant to use, and also sufficiently fool-proof will be the harder part.

Design goals - beginning (prototype Q3/2026)

My goals when I started this are to fit my purpose only, but to be flexible enough in design/architecture to be worth upgrading and making universal enough.

  • no scripting engine
  • no DSL
  • avoid writing BRE as long as possible (failed)
  • Rust, Bevy native
  • tremendous performance, no runtime reflection other than what Bevy is doing
  • avoid messy string key-value storage as much as possible
  • coding custom game features with normal Bevy code is good and expected (e.g. custom in-game reward for a story dialog option that’s not just a flag/variable)
  • support for Conditions and Consequences, e.g.
    • hide this dialogue option when energy is low
    • grey out this dialogue option when strength < 6
    • ask this question and get an answer only for the first time
    • show different question and answer every other time
    • allow for random-pool answers
  • misc Consequence features I want to have
    • straightforward dialogue
    • sound cue and music change
    • sprite animation (“john doe bustup enter stage left”)
    • allow for Tracery format sentences
  • everything relevant has custom IDs
  • mapping domain IDs to Entity id with secondary index
  • no storage format at first; Rust only, always-recompile is fine for now
  • make sure design allows for procedurally added story elements

Not in scope:

  • fully featured GUI for everything
  • pretty design
  • voice narration, lip-syncing, progressive text typing
  • easy CRPG dialog trees, although technically possible
  • scripting
  • articy, renpy, yarn spinner, twine import/export of any kind

Design goals - hypothetical mature stage (2 years of work)

  • provides stock egui or bevy_ui implementation, but fully replaceable
  • reasonably easily pluggable into any Bevy game
  • stable and reliable save format for the whole story
    • track discarded IDs so that deleting and adding a character, and then loading a game doesn’t corrupt data
  • stable and reliable save format for the game state
  • 90% feature parity with Ren’Py and Yarn Spinner
  • compatibility or one-way migration tools from Ren’Py and Yarn Spinner
  • incremental sync with Articy exports (i.e. adding/removing/renaming characters won’t break everything)
  • hot reload option in debug builds
  • allow for different pre-defined modalities - DnD, SPECIAL, Scarlet Hollow (Copyright allowing!)
  • have a running, fun, non-theoretical, non-trivial example of procgen dynamic storytelling
    • i.e. a game prototype where player actions can spawn dynamic story elements that might be a little silly and formulaic, but are still better than whatever Skyrim 18th re-release is doing these days
    • no LLMs anywhere in the process
  • implement branched quest lines
  • synthetic benchmark performance:
    • 1000 chapters, 10k characters, 100k story beats,
    • 1M story elements, 5M conditions and 5M consequences
    • 20M story variables (mix of global and per chapter and per character)
    • response time for any given story element <1ms

Design goals - open source (2-3 years of work)

  • Ideally would have a WASM editor which would allow to create stories and export into a stable+versioned format
  • optional human-readable export format (BSN possibly)

Design goals - commercial product (2-5 years of work)

  • wasm editor above, but more powerful?
  • definitely an image editor at least for simple drafts
  • publish stories them into a “cloud” for very cheap (free tier? free play for small chapters? storage caps?)
  • and generate on demand finished games for android/win/linux and maybe if $$ then mac/ios if feasible at all
  • desktop Editor should have powerful integration with Rust codebase, i.e. the ability to track enums and systems and offer these from drop down menus as different Conditions and Consequences

Design goals - megalomaniac (5+ years)

  • rival Ren’Py and Yarn Spinner, but without their insane text formats
  • replace Articy Draft as the industry standard

No that’s it, just two goals, I’m humble like that. /s

Jokes aside, I have a lot of respect for Yarn Spinner and Ren’Py, but I find their text formats ugly and I want to break away from that.


  1. Hard problems are things like physics engines, compilers, dealing with Google, optimizing equally for desktop as well as small screens, etc. Cursed problems are usually game design related, which often have no “right” solution, like making a multiplayer game that groups you with strangers that’s resistant to griefers, or making a chat filter that doesn’t censor “assassin”.

Architecture of the storytelling engine

In short: StoryManager is the Bevy Resource which

  • loads and saves user data (game save),
  • and story data (game content),
  • keeps a secondary index of every story related object,
  • and stores the current progress.

Everything else is a Bevy Component. This allows gradual development of new features without having to refactor a large unwieldy struct/enum/system.

Some objects might reference assets (character thumbnail, bustup, audio). Likely just a String with the file path, and not another layer of abstraction, although that might change to offer either - String for small games with limited assets (most VNs, JRPG), indirect reference for complex games that need to manage assets in different complex ways (3D CRPG, etc).

Every object has a custom newtype ID - u64. In alpha, it’s hardcoded in Rust code. When editor is available, it will be created and maintained by the editor and unique to the story, i.e. once assigned ID will never be reused, to prevent save corruption on game update. Future format will likely be BSN1.

Most trivial example

The game must keep track of conditions necessary to trigger a story somehow. The exact membrane doesn’t have a stable API yet, and the current idea is that

  • A) for small projects, this will be somewhat hardcoded, which is my use case now, e.g. “trigger story beat id 10” or hopefully more ergonomically, game sends a message like “Meeting Character John Doe”, and StoryManager will load mapping between messages (Triggers) and story beat Ids, and potentially other conditions (is in location X, etc)

  • B) As the project matures, and as Bevy gets a first party editor, there will be a plugin for the editor that will link game events to Story Beats (main container for small chunks of story).

  • Either way, a Story Beat Id = 1 will be started by StoryManager.

    • StoryManager will set something like current_story_beat_id = 1
    • StoryManager will record this being in progress
    • later, after finishing, StoryManager will also save this to history of already visited nodes
  • That will in turn load the initial Story Element = 2 and check Conditions (3,4,5)

    • ?????????????????????????????
  • In Bevy, Story Element entity will have components StoryElementId, and one of different StoryElement structs,

    • e.g. CharacterAnimationElement will move a sprite on screen (enter stage left)
    • in the future, the story engine will provide default canonical implementation of handling each StoryElement
    • but they’ll be each fully programmable to suit game’s GUI
    • one handler can handle as many different aspects as it’s practical, but often just 1
    • they’re just Bevy systems

  1. Bevy Scene Notation

Objects, structs, enums