Bardic: a Python-first Interactive Fiction engine for complex game state with visual graph-based story editing and live passage preview

Hello! I’ve been working on Bardic, a new IF engine/language that bridges the gap between interactive fiction branching narrative and the power of a full programming language. If you’ve even been tinkering with a macro or function in Twine or Ink and thought “I wish I could just import custom Python classes into my story to handle all of this!”, Bardic might be for you.

I was building a game that required lots of complex game state modeling and got frustrated with primitive variables in Ink, and all the macros (and whitespace handling) in Twine, so I made Bardic.

  • Bardic lets you write stories with real Python objects and code, not just primitives. You can import your own classes, functions and methods into the story and use them.
  • It also has parameterized passages you can use (perfect for shops or NPC conversations) that accept parameters just like function arguments. You can pass around data behind the scenes easily.
  • The engine handles auto-serialization of your entire game state, including your custom Python objects, automatically. The game also compiles to JSONs so it’s portable and easily readable by just about any system.
  • It’s frontend-agnostic. It produces structured JSON data (just like Ink!) and the engine ships with templates for NiceGUI, Reflex, and React+FastAPI. You can choose one of these frontend stacks, or bring your own, as long as something in your system can run Python!
  • The syntax is clean and Ink-inspired, so it’s mostly there to get out of the way while you write. BUT you can drop into pure Python blocks inside the narrative files, whenever you need it!

Development experience:

  • 60-second setup with the built-in bardic init - you get a working browser-based game immediately. It’s built on NiceGUI by default, which gives you a modern, interactive web-game. Other template options give you Reflex or React+FastAPI implementations out of the box.
  • VSCode extension with code highlighting and folding, snippets, and a full interactive node graph of your story that you can click on to navigate to passages in the .bard source file. (This is similar to Twine’s visual editor!)
  • VSCode extension also has a live preview from any passage feature that allows you to preview the rendering and appearance of any passage (even deep into the story) while allowing you to inject game state variables as needed. It’s been great IME for quick debugging and QA in long stories.
  • CLI tools for compilation to JSON and terminal play (mostly for testing things out as you develop the game).
  • Clean syntax with ~ one-liners and @py: blocks for full code.

Here’s some screenshots of the VSCode extension at work:


An example of the syntax:

# Import your own Python classes, just like in a .py file
from my_game.character import Player

:: Start
# Create a new Player object
~ hero = Player("Hero")

Welcome to your adventure, {hero.name}!
You have {hero.health} health.

+ [Look around] -> Forest
+ [Check your bag] -> Inventory

:: Forest
The forest is dark and spooky.
~ hero.sprint() # Call a method on your object
You feel a bit tired.

+ [Go back] -> Start

:: Inventory
# Use Python blocks for complex logic
@py:
if not hero.inventory:
  bag_contents = "Your bag is empty."
else:
  # Use list comprehensions, f-strings...
  item_names = [item.name for item in hero.inventory]
  bag_contents = f"You have: {', '.join(item_names)}"
@endpy

{bag_contents}

+ [Go back] -> Start

My Use Case:

I build a narrative card-reading game (80k+ words of .bard files) where players influence their clients’ lives through interpretations and their own choices. Every card in the deck is a Python object with properties and methods and the narrative needed to interact with them naturally.

How to get started:

I’ve got a quickstart guide in my repo’s frontpage readme but here’s a quick guide:

pip install bardic[nicegui]
bardic init my-game # defaults to nicegui template
cd my-game
bardic compile example.bard -o compiled_stories/example.json
python player.py

And then your game runs at localhost:8080! That’s really all you need to do to get up and running!

Tutorials:

I wrote a full tutorial series to get you started (with separate paths for people who know python, and people who have never touched python in their lives but want to write a game). Documentation and ramping the user up from “zero” was really important for me. The tutorial takes you from writing simple branches (like Twine) all the way to creating custom Python Classes and economy systems, even if you’ve never coded before.

Check it out here: bardic/docs/tutorials/README.md at main · katelouie/bardic · GitHub

Links:

I would really love to know what the community thinks, and if you’re interested! I’m happy to answer any questions about design or technical details, or how to get started writing with Bardic. Also very interested in feedback about anything – engine, language, feature set, tutorials, dev tools like the VSCode extension, etc!

28 Likes

Hi Kate,

Thanks for joining the community! We’ve had a lot of people announcing new systems here recently but this one looks pretty unique and has a solidly reasoned use case, which I like.

My initial question is related to this:

People have submitted Python games to the big IF competitions before, and the lack of easy web play out of the box has meant they don’t get much attention. Is there an easy way to generate a web version of your game with Bardic? Because if so that could be a game changer!

(Pardon me if that’s answered elsewhere in your post or on the documents you’ve linked, my dev skills aren’t that great.)

4 Likes

Hi! Thank you for the welcome! :smiley: That’s a good question, and it’s got a couple answers:

The Current Way

Bardic comes with a few different pre-built webapp templates “out of the box” (niceGUI, Reflex, or React+FastAPI) that are their own self-contained web versions of the game. (You can get the template for your new game by just running bardic init my-game and it sets it all up for you locally, already integrated to play whatever bardic files you decide to put in your project.)

What I’ve done for my personal game I mentioned in the post, in order to get it on itchio, is host it on Railway (or anything else similar for easy hosting from a github repo). Then I uploaded a very small iframe wrapper HTML file up onto itchio. You can see an example of that file here: arcanum-game/itchio/index.html at main · katelouie/arcanum-game · GitHub . And you can play the proof-of-concept game as a web browser game on itchio with just that file uploaded.

So that’s one way to quickly get it up and running as a web version. The file itself just needs a single-line replacement to point to wherever your webapp is running.

The Possible Future

The second option is one I’m still trying to work out the particulars of – using PyScript or Pyodide to embed Python into a single HTML file (much like Twine and JavaScript). This is kind of tricky, because PyScript is still pretty new (and I’m new to it too). But it’s much more self-contained and most likely more straightforward for writers.

Definitely open to thoughts and feedback here! I want to make Bardic as “friendly” to devs as possible, from starting up all the way through to the distribution process.

4 Likes

I have never wished I could include Python in my game, but I am certain others have, and this seems like a really thoroughly thought through and realised engine! Nice work.

2 Likes

Thank you! I really appreciate it! :slight_smile:

1 Like

For reference, there’s about 20 results that come up if you search IFDB for games written in Python, so while that’s not a ton there’s clearly a desire!

I’m going to ping @aschultz as I know he’s written at least one comp game in Python before - I’d be curious to know his thoughts on Bardic.

3 Likes

Hello and thanks for announcing your system. It does look very interesting.

I agree this is a good way to inject custom logic into choice based narrative. Overall, it depends if you like Python or not and whether people think that makes it too programming-like or not.

You’re going to have to solve the web problem of making it run client side. This might be the biggest python problem. If it were me, I’d pursue the WASM line.

Are there plans for pictures and sound? As that would be very nice. But then, you might be on collision course with Ren’Py. Or is there another way?

In any case, great work and best of luck.

Hi! Your repo lists Claude as a contributor to Bardic. Can you explain what that means?

There are IF competitions that explicitly forbid entries that use generative AI, so that would be helpful to point out for potential authors that use this.

3 Likes

Hi! Good question.

  • The “Claude contributor” being listed is due to about 5 commits where I asked Claude to write my commit messages, in which case it tags itself as a co-author and shows up as a contributor to the repo.
  • I did, to be honest, pair-program with Claude to get some of the new pytest tests, flesh out docstrings, and write some documentation. I also used it to help debug some of the thornier parser problems I ran into, as well as work out the more complicated edge-case regex patterns, which I am admittedly not great at. None of that should affect the content or format of the game itself.
  • For competition authors: Bardic itself is just a parser and compiler – it doesn’t add or modify any text other than in things like conditionals or variable expressions in .bard files (for which the user explicitly writes the logic). Any content in the game is entirely the writer’s own creative work. That said, if a competition’s AI rules are very strict (such as not allowing the source code for the compiler of the language the game is written in to have AI contributions), it’s definitely worth checking whether that falls within their guidelines. I’m honestly not very familiar with the level of stringency of a lot of IF competitions, and if it reaches that far. But people are always free to not use Bardic, especially if they have ethical issues with any and all utilization of generative AI.
2 Likes

Thank you! Yes, it does kind of hinge upon if Python blocks are too “code-y” for people and it puts them off.

The way I chose to separate concerns re: visuals and audio is – Bardic is the narrative engine, and the frontend handles frontend stuff. However, I have implemented a @render directive that tells the frontend to handle custom presentation logic, and sends formatted data one-way to the frontend from the story. (bardic/docs/spec.md at main · katelouie/bardic · GitHub) It already has a @render:react framework hint that custom-formats the data into a React-appropriate format for ingestion, but you can use it with whatever frontend you want as long as you write the functions for it to ingest that data.

And yeah…the client-side issue is the big one. I’ve been looking into things like py2wasm, it’s just difficult because my engine parser, compiler and game navigation engine are written in python. It’s just a little grimace-y to work with non-Python code after all this time in my favorite language, but at some point I’ll have to face up to it. I’ve been putting it off a bit, working on getting Bardic feature-complete, but it’ll probably be the next big hurdle of implementation.

That’s helpful to know, thanks. I appreciate the honest response.

Looks like it’s pretty close to being Ink, but with a full scripting language? I have a couple main questions:

  • Does it have an equivalent to the “threads” feature in ink (allowing you to collect choices from multiple different knots)?
  • Are the Python blocks able to generate choices, or just manipulate data & produce text output?

I can’t help but notice that the “reusable passages” shop example still requires the player to manually write separate “buy wheat,” “buy carrot,” and “buy hoe” choices, rather than being able to do that in a @for loop or something, which seems like it would be annoying.

1 Like

This is exciting. For years I’ve wanted to try my hand at writing some choice-based games, since none of my parser-based projects are even close to done, and I want to tell stories. Being blind, though, it’s all a little trickier with the amount of visual editing, which seems to be the most popular and supported workflow for this sort of development. It’s not the only one, of course, but I’m always on the lookout for systems that let me leverage my existing skills as a programmer. I’m pretty experienced with Python so I’m looking forward to giving Bardic a shot. My only question would be about templates and web presentation: How easy is that stuff to customize in the engine? My Python experience starts running out around where it gets into the web stuff (I spun up a Flask once). I’m happy to teach myself new skills, it’s just going to be important to me that I have control over the elements of the generated page, for accessibility purposes mostly. I’m sure that’s a thing already, just kinda curious about the workflow.

Thanks for posting!

3 Likes

Given that the average Twine/SugarCube author on the Twine discord (perhaps not representative of the average user, but certainly the average “pro”) is probably shoving JS blocks into every second passage, I think people are plenty happy to just drop into “pure code” when needed.

6 Likes

I’m strongly drawn to the graphical story editor in the VS code extension! How does this work? Can you round-trip story design and code? Does the SVG export contain simply the graphical elements or does it capture a data model of the story?

I was definitely really inspired by Ink’s syntax and wanted to emulate the elegance and simplicity of it, but with Python capabilities layered on top. So you’re on the nose there!

It doesn’t currently have a feature like that – I think they may be called “Gathers”? It operates more like Twine in that way. I originally was developing the language to “scratch my own itch” and didn’t find that I was using gathers really, so I left it out. Do you find yourself using that functionality a lot?

And thank you for pointing out the issue in the reusable passages part of the docs! That’s actually rather out of date, I need to update it. So thank you for bringing it to my attention! You can generate choices in @for@endfor logic blocks when using reusable passages as the target. You can see it in action in this example story about a wandering merchant: bardic/bardic/examples/wandering_merchant/merchant.bard at main · katelouie/bardic · GitHub .

I’m happy the language’s development style fits with your workflow and process!

So, the templates are actual python files for NiceGUI (etc, etc.). The reason I actually went with NiceGUI as the default template is because, quite frankly, I don’t enjoy frontend development much and NiceGUI (and Reflex to a lesser extent) are pretty much “frontend but for Python devs who hate frontend.” It’s pretty friendly in terms of placing and re-arranging page elements where you want them, and it has the option to use stuff like Tailwind (or Quasar) classes to do all the styling.

Here’s the actual code file for the template NiceGUI that gets generated via bardic init: bardic/bardic/templates/nicegui/player.py at main · katelouie/bardic · GitHub . You can access it directly from your generated project directory and tweak or add to it however you see fit.

And for a more in-depth example of NiceGUI making a “fancy” website/webpage with detailed layouts and precise styling, here’s the full nicegui-only code for a different project of mine: stellium/web/pages at main · katelouie/stellium · GitHub. Take a look and let me know if you have any questions!

1 Like

So, I’ll try to answer your questions as best I can!

Graph view: The graph view in the vscode extension is a read-only visualization (built with visjs) that runs in a VSCode webview panel. When you open the graph view, the extension compiles your .bard source file (see on the left) to a AST-like JSON and extracts the story data (passages, choices, jumps, etc) to create its nodes-and-edges structure. It’s a one-way sync only at the moment – when you save your .bard file, the graph view updates, auto-recompiles and refreshes. You can click any node to jump to that passage in the editor, but as of right now you can’t drag nodes around or edit the story via popups in the graph panel.

(My reasoning: not wanting to have to store hand-selected coordinates in the .bard file when that data isn’t really “part of the story”; the fact that visjs auto-arranges nodes and edges with a physics engine already; and that passages often get so long that editing them in popups in the graph panel was clunky and it was much smoother dev process to just auto-jump to the source code on node-click).

So the workflow is: edit text file → save → graph updates → click node → jump to source passage → etc.

SVG export: The SVG contains visual elements only (the rendered boxes, lines, labels, colors, etc.) However the JSON that the extension auto-compiles your story to (which you can also generate yourself via bardic compile myfile.bard -o output.json) is a full representative story-and-content structured data model, which you can use as you like. It’s actually what Bardic’s engine uses to navigate through and render the story properly.

I’d appreciate any feedback you have on this feature set!

1 Like

I do use the “threads” feature in Ink a lot, primarily because it’s the only way to programmatically generate a list of choices or to gather up choices from multiple different knots (which, confusingly, is not what a “gather” is), but it might not be necessary if you can put choices in a @for block, I guess.

Yeah, Ink sometimes gets a bit carried away with its textile metaphors…

3 Likes