Rez is a simple, declarative, language for creating choice based games & interactive fiction. The syntax is easy to learn being largely composed of elements and attributes.
Rez uses Javascript functions to implement dynamic behaviours. JS functions appear as attribute values for handling events and can also be used for implementing custom components.
It’s relatively simple but designed to make large/complex games manageable.
It includes asset management, a renderer with support for different scene styles, a template language with user components, support for actors & behaviours, items & inventories, and more.
Here is a short example that nevertheless covers a number of features including event handling, forms, dynamic templates, element references, scenes & card layout, and more.
%% Include the standard library
%(stdlib.rez)
@game {
name: "my_game"
title: "My Game"
author: "Matt Mower"
author_email: "self@mattmower.com"
game_homepage: "https://rez-lang.com/"
IFID: "B37043A4-6362-11F0-989B-CA41670CF0BC"
archive_format: 1
version: "1.0"
created: "2025-07-17 23:06:41.694789Z"
initial_scene_id: #scene_one
layout: ```
<!-- Main Content -->
<section class="main-content is-fullheight">
<div class="container">${content}</div>
</section>
<!-- End -->
```
}
@scene scene_one {
initial_card_id: #whats_your_name
}
@actor player {
$global: true
name: _
}
@component error (bindings, assigns, content) => {
if(bindings.card.error !== "") {
return `<div>${bindings.card.error}</div>`;
} else {
return "";
}
}
@component live_input (bindings, assigns, content) => {
let {type, name, bind} = assigns;
return `<input type="${type}" name="${name}" rez-bind="${bind}" rez-live class="input" />`;
}
@card whats_your_name {
error: ""
content: ```
<p>This game is called ${game.title}.</p>
<form name="player_form" rez-live>
<p>What is your name: <.live_input type="text" bind="player.name" /></p>
<.error />
</form>
```
on_player_form: (card) => {
if($player.name.length > 0) {
card.error = "";
return RezEvent.playCard("your_name_is");
} else {
card.error = "You must enter a name!";
return RezEvent.render();
}
}
}
@card your_name_is {
liked: false
on_render: (card) => {
card.liked = Boolean.random();
}
bindings: [
player: #player
]
content: ```
<p>Your name is ${player.name}. What a $if(card.liked) -> {% nice %} (true) -> {% horrible %} name!</p>
<a card="whats_your_name">Try a different name</a>
```
}
Major changes in v1.8
Replaced built-in node validation with the @schema directive (see stdlib for examples of schema directives for built-in elements)
@item is no longer a base element but an alias of @card which demonstrates that aliases work well.
RezInventory allows for adding anything with a type: attribute
Removed the table attribute data type
Pass params to game scene_start events
Pass params to scene start events
Started documenting the stdlib
Updated tests
Game start_events is now handled as a bindings list
Updated to Ergo 1.0.4 and improved attribute parsing error messages
The enter key when pressed in the input of a rez-live form now triggers the ‘submit’ event to its card
I started my previous thread in Jan 2024 and it’s grown to be several hundred replies & over 30-min reading time, so I don’t feel too bad about starting a new one.
There’s only one code change: I finally fixed the names of the stdlib functions for converting strings between different cases. They were all over the place and now all the same, e.g. toCamelCase().
What I have done is a lot of updates to the documentation. I completed the stdlib documentation and fixed up the tables of contents which weren’t rendering (or rendering properly) across all the documentation.
Adds the ^c:#id copy initializer. This completes the basic picture on “id based programming” for procedural content. Here’s the simplest possible example:
By marking the skill object as a template the initialisation of the level: attribute doesn’t happen when the object is created, rather it gets deferred until the object is copied. The ^i{} initialiser says “initialise the value of this attribute to the result of the given Javascript expression”.
The new ^c: prefix says “this attribute value will be initialised with the id of a copy of the named object”. When the player is initialised it will copy sk_shooting, creating a new object (with id sk_shooting_1) whose level: will then be initialised with a value from 1 to 10 (biased towards the middle of that range).
So now you can connect together copies of objects into a whole assembly. There are still some rough edges and syntax that could be improved, but I’m making use of it.
You might be wondering, why not something simpler?
@actor player {
name: "Steve"
shooting: ^i{return Math.cl_rand_int_between(1, 10);}
}
Wouldn’t that be much simpler? And you’d be right. For simple examples it is simper and more direct to work directly with the attribute value.
But as I have started working with procedural generation of NPCs I have found that a composition strategy helps tame the complexity. For example in Fleet Commander different kinds of NPCs have different “skill packages” each representing an officer class and composed of a mixture of skills where there is a lot of overlap.
A number of fixes and small changes were also made that are listed in the release notes.
I’m working on what will be Rez v1.8.4 which introduces two changes, one invisible and one visible:
In working with the behaviour trees I found that they could be simplified. Rez’s behaviour trees were a port of my prior Clojure behaviour tree library. The insight is that Rez game elements already form a blackboard for preserving/sharing state, but i wasn’t using them as such. v1.8.4 does and simplifies how the whole system works.
As an aside I’ve also settled on implementing “behaviour-tree-per-element” rather than trying to “blend” different trees into a single unified tree. It seems there is ample prior art here so I feel on safe ground. The one remaining question is the ordering of trees. This matters in cases where agents are coordinating because “who goes first” can have an effect on the outcomes/narrative. I don’t see an obvious winner here. In FC I am tending towards randomising the officer list on each run although that has its own downsides.
The visible change is the introduction of the @const directive that creates both a compile time & runtime constant.
@const ultimate_answer = 42
Will define a constant that can be referred to in attribute values:
console.log(`The ultimate answer is ${$ultimate_answer}`);
At the moment it only supports bool, number, and string values although there is a case for letting it support any legal value.
It’s a small change but I was starting to accumulate “magic numbers” in my source and this lets me lift them out, making things more readable, as well as making it easier to refer to the same values within JS event handlers.
I have now released v1.8.4 which, among a number of things, adds the @const directive and, as I prophesied, you can now make any legal value (in particular lists) a const. There may be some types that won’t translate into very useful JS values but then I can’t see any good reason you’d make one a const.