Arcturus - a new programming language & compiler for the Z-machine

Hello everyone,

some of you know me from my games, the Hibernated series, The Curse of Rabenstein, or Ghosts of Blackwood Manor. Today I want to show you what I have been building for quite a while now: Arcturus, a new programming language for interactive fiction, with its own compiler that emits standard Z-machine story files.

The idea behind Arcturus is simple to state and was challenging to achieve: give the author the comforts we know from modern systems like Inform 7 and Dialog, but compile them down to very efficient Z-code, so the same game that feels contemporary to write also runs well on an 8-bit machine. Big things with little code and syntactic sugar. That’s the fundamental idea.

What it looks like

Arcturus never pretends to be English, and it never makes you fight punctuation either. Every construct is one keyword led line on an indent skeleton, so a stranger can open a game’s source and follow the story:

room shop
    name "The Tick-Tock Emporium"
    desc "Clocks cover every wall, disagreeing politely about the minute."
    north workshop

thing coin in shop
    name "silver coin"
    words coin, silver
    desc "One honest coin."

Behavior is handlers, and I consider them the heart of the language. A handler lives on an object, a kind, a room, or free at file level, and an action walks that chain most specific first. One mechanism covers what elsewhere takes instead rules, before and after rulebooks, daemons, and life routines:

on take when clockmaker is not paid
    change refused to true
    say "The clockmaker's eyes never leave you. One coin, first."
    stop

on after take
    say "The carriage clock ticks against your palm, keeping your pulse now."

every 4 turns do clock_chimes

on each_turn when player holds fish
    say "The cat pads after you, green eyes fixed on the fish."

The same shape carries a surprising distance. Arcturus follows the modern darkness model (in the dark you can still feel what you carry, so INVENTORY works, but EXAMINE and READ refuse), and if you want it stricter, Inform 7’s “Instead of taking inventory when in darkness” rule translates very easy to Arcturus:

on inventory when is_lit is false
    say "It is far too dark to rummage through your belongings."
    stop

You will find more of these modern comforts throughout: vary for self-varying prose, beyond for reach modeling, computed descriptions, multi-room scenery, doors, locks, kinds with inheritance. And some things I believe exist in no other system: grains, scenery words that answer verbs without any object behind them, so a room can be dense with detail at almost zero cost; a container model that tracks what the player has actually seen and words itself accordingly; auto-scoring where the compiler sums your maximum score at compile time. Scope, by the way, is something you will almost never think about. The library handles it quite well. I think :smiley:

The compiler is called arcc and currently handles Z-machine version 5 and version 8. There are no plans to support other Z-machine versions. For retro systems, v5 is all you need and the modern author will be happy with the ceiling that z8 provides. But feel free to change my mind :wink:

Cosmos, the Arcturus standard library

The standard library, Cosmos, is written in Arcturus itself and ships as editable source, not a black box. To change a standard message you redefine its block (a block is what some of you consider a routine) in your own story and yours wins, or you just fork the English language pack, which you can easily do, as the parser is language agnostic. But more on that below. Optional features are granules you summon (summon is what you know as include or import in other languages): two complete conversation systems (classic Infocom ask/tell and a menu-driven one), a status line, verbose exits, extended verbs, nautical directions, ambience and more. What you do not summon is simply in your story. Modularity was a very important factor in the creation of the Cosmos standard library.

That last part is the compiler’s doing. arcc is a whole-program, multi-pass compiler with strict dead-code elimination: it sees game and library at once and a build carries only what the game actually uses, provably, byte for byte. Text compression is automatic, every compile prints a statistics ledger of your Z-machine headroom, and --zversion 8 targets version 8 for large releases. Tailored abbreviations can be created through a compiler flag. The compiler is pure Python, standard library only, one self-contained file (including the standard library): download, chmod, done, on any OS. If you want to fork something form the library, you can eject the corresponding .granule file.

Why Cosmos you may ask? Well Arcturus is a star and the narrative arc every story is built on. The game file is a .storyarc file. And in cosmology, where the Cosmos is the environment of stars, the library is the environment of your story. Also, I found it funny, should I ever release a minimal version of the library, I could call it MicroCosmos. On a side note: granules are the convection cells that tile the Sun’s photosphere, since Arcturus is a star, a summoned module is a .granule on its surface.

Pictures (yes pictures, and this is not .z6)

This one I am a little proud of: Arcturus games can carry images in z5 and z8 story files while staying fully standards compliant. The pictures ride an extension opcode in the range a conformant interpreter must simply ignore, and a story only draws after an interpreter raises a capability flag that picture-aware interpreters alone set. So the opcode call is hidden from an interpreter not supporting the format. The same file plays as pure text on Frotz or any classic interpreter, and shows its art where art is understood. No Blorb or separate builds.

Note that arc_image supports two picture modes: 9 lines and 12 lines height. The 9 lines height (72 pixels) is equivalent to Infocom’s Arthur, and the 12 lines (96 pixels), almost half of the screen, is what you know from environments like DAAD.

Actaea and arcimg

Actaea is the reference interpreter, also a single Python file with zero dependencies. It plays any well-formed v5 or v8 game (not just Arcturus output) in a window or in the terminal, does Quetzal saves that interoperate with Frotz both ways, and doubles as a debugger and disassembler. It renders arc_image pictures today; a set of interpreters for retro systems with arc_image support is in the making. You already saw a few pictures of Actaea throughout this thread. Here is one in --console mode from my MacOS Terminal.

Actaea is fully standards 1.1 compliant, fully verified also with CZECH, @zarf 'sTerpEtude and Praxix. Note that Actaea currently, as Arcturus itself, supports Z-machine version 5 and version 8. Here you can see it running Jigsaw through the UI.

arcimg completes the toolchain: you paint one master image, and the tool converts it to the ideal native format of every retro target, fifteen machines so far, each conversion tuned to what that hardware does best.

And if you write in VS Code, a syntax highlighting extension for the Arcturus file types ships right in the repository, with install instructions in the README. Your game source, the Cosmos library, and the granules all have proper colouring.

Languages, docs, examples

The parser is language agnostic: grammar and wording live in a language granule, and English, German, and Spanish ship with the compiler. German is my own work as a native speaker, and the Spanish granule was built together with Pablo Martínez, whom many of you know from the Spanish IF scene or from this forum as @Kozelek. You can eject the language granule and adapt it to your own tone, or your own language, without touching the parser’s machinery.

The documentation is complete: a full syntax reference with two worked games (including the classic Cloak of Darkness), a runtime reference, and guides for the granules, the interpreter, and the image workflow. Alongside them, over thirty small example games each isolate one feature.

Where to get it

Everything lives here, MIT licensed:

The whole toolchain is two self-contained files (a third if your game uses pictures). All you need is Python 3.11 or later. The README’s Quickstart has you compiling and playing in about a minute.

A word on how Arcturus is built

I want to be very open about how this project, so I am going to post what is already written on my repository (kinda): the language design, the syntax, Cosmos, and the compiler are my work, and Arcturus is a human-driven project. I use Anthropic’s Claude Code as a coding assistant. It doesn’t do anything I couldn’t do myself. Python is my language of choice, and I use it daily in my role as regional head of a department at a global software company. Arcturus is a serious effort and I build it the way any engineer would build a project of this scope today. I know some of you have mixed feelings or objections about AI, and I take that seriously. For me, a coding assistant buys the time and the efficiency that make a project of this scope possible for the community, and what it enables in the end are new opportunities for interactive fiction and new, wonderful human-made stories. That is what matters most to me. The repository keeps a full log of my design decisions and the code I made along the way. This is not another Sunday-afternoon-vibe-coding-experiment but a thoughtfully crafted product with a full-featured roadmap and quality assessment through regression tests, where the human factor in the loop makes the difference.

Join the communtiy

If you want to talk: we have a young but very active community on Discord, with early adopters already writing real games in Arcturus and throwing a plethora of bugs at me like @improvmonster, shaping it significantly with their feedback. Come join us:

I am happy to answer everything here in the thread as well.

Stefan

18 Likes

Interesting! I have a few questions that don’t seem to be answered in the documentation, though.

How much of the IF paradigm is built in at the compiler level vs the library level? In I7, for example, it’s effectively impossible to reimplement containers and supporters in a custom library, because they’re built into the compiler at a fundamental level. In Dialog or I6 that’s not the case. In I6, on the other hand, the concept of an action is built into the compiler; in Dialog it’s not. It looks like in this case, a lot of the parser machinery is built in, with language builtins for things like “words that separate multiple commands in a single line”?

I’m a big fan of adding graphics support to Z5 and Z8, but I’m curious why you decided to not support blorbs, and instead distribute a Z-code file alongside a folder full of PNGs. There’s been a standard way to distribute a Z-code file with a bunch of PNGs for decades now; why not use it?

5 Likes

Amazing and congratulations!

1 Like

The split leans towards I6 and Dialog. The compiler owns the byte-level stuff: object layout, the dispatch tables, grammar tables, dead code elimination. Behavior lives in Cosmos, which is ordinary Arcturus source you can extract and fork. That includes containers and supporters, so yes, you could rewrite them. The command separators you spotted are also not builtins, they’re blocks in the language granule. That seam is how German got its recipient-first grammar and Spanish its clitics without me touching the parser skeleton. Those are library integrated translations notably.

The PNG folder is only meant for debugging, a release ships the .z5 plus a single .arcres pack that arcimg generates. I should make that clearer in the docs. It is visible though in the examples folder. Good question on Blorb: my pictures have to reach interpreters on real 8-bit machines, and a C64 won’t unpack PNGs out of an IFF container mid-game. Every target needs its native encoding, so I’d own a retro format either way. I’d rather own one format than two, and it goes all the way down: fifteen targets so far, and the first community-built retro interpreter with arc_image support is already in the works. :slight_smile:

1 Like

Thank you very much, much appreciated!

Sure, but retro machines won’t be running Python 3 or looking at PNGs at all, right? My understanding is that Commodore 64 games are typically distributed as disk images anyway; why not use blorbs for the modern desktop Python interpreter, since that’s what other Z-machine interpreters will expect?

Personally I would love it if image support for Z5 and Z8 became more widespread in general, but I think that’s more likely to happen if Z5-with-image files are distributed in a standard format.

1 Like

Right, the retro side ships as disk images, mastered by arcimg with each target’s native data. The .arcres pack only concerns the desktop.

But here’s the thing: a Blorb wouldn’t do what you’re hoping. Existing Blorb-aware interpreters wouldn’t show these pictures anyway, because arc_image doesn’t use the v6 picture opcodes. These are valid for v6-story files only. Arcturus produces a custom extension opcode plus a capability flag, so any interpreter out there has to implement the Arcturus drawing protocol before the container matters at all. And once someone does that, reading the pack is the easy part: it’s a zip of numbered PNGs, nothing exotic. What would make z5-with-images widespread is interpreters adopting the protocol, and that part is documented and open.

If that adoption happens and terp authors would rather have Blorb as the container, arcimg growing a Blorb export is a small step, and I wouldn’t be religious about it.

Very cool!

What would be necessary for another interpreter to be compatible? Just the one opcode and flag?

Do you have positional control over pictures or is it interpreter dependent?

Where is the capability flag?

1 Like

I absolutely love that Claude/GenAI has helped people with a vision reach their goals. Arcturus is an excellent modern language that looks like it fits right in, especially with the decision to compile to the z-machine.

1 Like

Pretty much, yes. The whole protocol is:

  1. The flag: if your interpreter can show pictures, set bit 1 of Flags 1 (header byte $01, mask $02) at boot, and again after restart/restore. That’s the v6 “pictures available” bit, which is meaningless in v5/v8 and stays 0 everywhere else. The game checks it at run time and never draws without it.

  2. The opcode: EXT:$80, two operands: picture id and mode. Id 0 clears the band. It sits in the private extension range, so an interpreter that doesn’t know it must ignore it per the standard, and thanks to the flag guard a text-only terp never even decodes it.

  3. Positioning is deliberately not free: it’s a band model. The picture band sits above the text, and the mode operand says how tall it is: mode 9 is 320x72 (the upper third, classic Arthur look), mode 12 is 320x96 (the upper half, the DAAD look). The interpreter owns rendering and integer-scales to its screen. That constraint is what lets the same protocol work in a desktop window and on a C64.

Resources: picture id N is N.png, shipped in a .arcres pack beside the story (it’s a plain zip, no manifest, the names are the index). There is a guide for interpreter developers in the repository by the way :slight_smile:

1 Like

Now I’ve doubled down. My system is ready to be released. Nearly 2 years of work. It is the documentation that is now my bottleneck. It is so exciting to be a part of the tool creation space.

1 Like

Nice reuse of the flag bit. My first reaction was “Wait, that’s the status line type flag.” then I remembered you are targeting z5 and z8 and that bit isn’t part of the standard for those versions.

1 Like

Claude is excellent at taking your system and creating a tutorial, developer manual, language reference, and actually test all the code in each.

Exactly! Thank you very much :slight_smile:

And yes, any interpreter that behaves according to the standards spec irgnores it. I should mention that if you don’t use the arc_image feature at all in your code, the compiler excludes the opcode from the story file completey.

1 Like

Claude by itself would not be able to do this with my system. It is a Visual Studio / Game maker with heavy use of UI elements. There still aren’t good ways to do this and there really never has been. Testing UI driven software automatically was a kind of holy grail at one time way back. Microsoft built in the ability to XP but had to remove it because it was being abused. edit - Anyhow I’ve got all the time in the world to do it and it keeps me busy. It’s like my job and I enjoy it.

FWIW, these exist in ZIL under the name “pseudo-objects”.

PunyInform also has a “cheap scenery” extension, though it is limited to the EXAMINE verb I think.

Thank you, David, that is very kind.

The vision is indeed the old bit. I have wanted a language shaped like this since the Hibernated days. I knew exactly how it should be like. What I never had was a decade of free evenings to build a compiler, a library, an interpreter, an image toolchain and the docs for all of it. That is really the whole story of why Arcturus exists now and not in 2034.

And it is not exotic where I come from either. This way is simply how software gets built in our company now. We are actively encouraged to run private projects to stay current with the models and the tooling. Fun fact: our CEO was visiting the Berlin office recently and spent a few minutes looking over my shoulder while I was debugging arcc. :slight_smile:

1 Like

Ah, I did not know that, and that is genuinely interesting. I never dug into ZIL, so I went and looked it up after your post: it turns out PSEUDO goes all the way back to Infocom’s own sources. Hitchhiker’s has a room carrying (PSEUDO "DOORMA" UNIMPORTANT-THING-F "MAT" UNIMPORTANT-THING-F), and The Lurking Horror does the same for a banner, a poster and some signs. So I was several decades late to the idea. Now I know better. :slight_smile:

The mechanism differs a little in Cosmos. Infocom’s version is a property on a single room, pairing words with one action routine that then has to sort out the verbs itself. A grain line names the actions it answers, so the same word can answer examine one way and touch another without a routine sorting out verbs by hand. Grains can also be attached from outside an object’s body, which is how a granule or a language pack adds scenery to a room it does not own. Same core trick though.

I will have to look into ZIL properly sooner or later regardless. I am the author of Puny BuildTools, and now that Arcturus is out, the package will likely transition into The Z-machine BuildTools. Once it builds both Arcturus and PunyInform projects for retro targets, ZILF belongs in there too.

It’s not. For each object it can provide either a string, which will then be the reply for EXAMINE, or a routine, which will act as a before routine, i.e. it can react to any action.

1 Like