usagi

Usagi - Simple, Rapid 2D Game Engine

Usagi is a simple 2D game engine for quickly prototyping simple games with Lua 5.4. It features live-reloading as your change your game code and assets. Its API is clear, consistent, and familiar.

Usagi is built with Rust and sola-raylib.

WARNING: Usagi is very early in development and not stable. APIs and commands will change.

Usagi is made by Brett Chalupa and dedicated to the public domain.

Install

Download the latest Usagi build for your operating system.

You can also install Usagi with cargo if you have the Rust toolchain installed:

cargo install --git https://github.com/brettchalupa/usagi.git

NOTE: the Windows build and installing via cargo don’t support the web target with usagi compile yet.

More ways of installing Usagi will be added in the future.

Hello, Usagi

You now have the usagi CLI that you can run from your shell (usagi.exe on Windows). Create hello.lua and run usagi dev hello.lua. Then edit the new file by adding:

function _draw()
  gfx.clear(gfx.COLOR_WHITE)
  gfx.text("Hello, Usagi!", 10, 10, gfx.COLOR_BLACK)
end

When you save hello.lua, the Usagi runtime automatically reloads it. Make changes to the text and see it live update.

In most traditional game development environments, you would need to restart your game’s executable after making changes. Usagi lets you focus on coding and making art without losing the current game state, allowing for much faster iteration cycles.

Need to revise a sprite quickly? Just open it in Aseprite, tweak it, save it, and see it update in the context of your game.

Project Goal

Usagi does not aim to be anything more than a rapid development engine for simple, lower res 2D games. It doesn’t intend to support mobile platforms or touch or VR. It doesn’t aim to replace Love2D or Pico-8 or Picotron. It’s not a fantasy console. It’s a command-line program and suite of tools to help you make games quickly.

Usagi is great for those learning game programming. And for those who to use something more flexible than Pico-8/Picotron but more constrained than Love2D.

Why Lua: Lua is a widely-used language in game programming, and it’s quite simple yet surprisingly powerful, making it a good fit for Usagi.

If you want to build a medium-to-large polished game, Usagi would not be a good fit.

Project Layout

A Usagi game is either a single .lua file or a directory with a main.lua in it. Optional assets live alongside:

my_game/
  main.lua        -- required: your game
  sprites.png     -- optional: 16×16 sprite sheet (PNG with alpha)
  sfx/            -- optional: .wav files, file stems become sfx names
    jump.wav
    coin.wav

Run with:

While developing Usagi itself, replace usagi with cargo run -- (for example cargo run -- dev examples/hello_usagi.lua).

Constraints

Usagi embraces a few constraints inspired by Pico-8 and Pyxel to help focus on prototyping rather than making polished high-resolution graphics. These may change in the future or be configurable.

You currently must bring your own sound effects and sprite editor. A sprite editor could be nice in the future as part of the usagi tools.

TODO - What’s Missing

Here’s what Usagi will support as it heads towards 1.0 release:

Lua API

Callbacks

Define any of these as globals; Usagi calls them:

function _config()
  return { title = "Snake", pixel_perfect = true }
end

_config() runs before the runtime is fully alive (the window doesn’t exist yet), so its return value is read once at startup and cached. Editing _config() while the game is running won’t update the title or any future config field on save; restart the session to pick up changes.

gfx

Drawing. Positions are in game-space pixels (320×180). Colors are palette indices 0-15; use the named constants.

input

Abstract input actions. Each action is a union over keyboard, gamepad buttons, and the left analog stick; the first connected gamepad is used.

Action Keyboard Gamepad
LEFT arrow left / A dpad left / left stick left
RIGHT arrow right / D dpad right / left stick right
UP arrow up / W dpad up / left stick up
DOWN arrow down / S dpad down / left stick down
CONFIRM Z / J south + west face (Xbox A/X, PS Cross/Square)
CANCEL X / K east + north face (Xbox B/Y, PS Circle/Triangle)

input.pressed is edge-detected on keyboard and gamepad buttons but not on analog sticks; track stick state in Lua if you need that.

sfx

usagi

Engine-level info.

Indexing

Sequence-style APIs (gfx.spr, and any future sound/tile indexing) are 1-based to match Lua conventions (ipairs, t[1], string.sub). gfx.spr(1, ...) draws the top-left sprite.

Enum-like constants (palette colors, key codes) keep their conventional numbering. gfx.COLOR_RED is 8 because that’s its Pico-8 number, not because it’s the 9th color.

Live Reload

Usagi watches the running script file and re-executes it when you save. The new _update and _draw take effect on the next frame — your current game state is preserved across the reload so you can tweak logic mid-play without losing progress.

Writing Reload-Friendly Scripts

The chunk re-executes on save, so any top-level local bindings get fresh nil values each time — callbacks that captured them as upvalues will see nil and crash. The pattern:

See examples/hello_usagi.lua and examples/input.lua for the layout.

Tools

usagi tools [path] opens a 1280×720 window with a tab bar for the available tools. The path is optional; pass a project directory (or a .lua file) to load its sprites.png and sfx/ assets. Without a path the tools open with empty state.

Switch tools via the tab buttons or with 1 (Jukebox) / 2 (TilePicker).

Both tools live-reload their assets: drop a new WAV in sfx/ or save a new sprites.png and the tools pick it up on the next frame.

Jukebox

Lists every .wav in <project>/sfx/ and lets you audition them. Selected sounds play automatically on selection change (Pico-8 SFX editor style), so you can just arrow through the list to hear each one.

TilePicker

Shows <project>/sprites.png with a 1-based grid overlay matching gfx.spr. Click any tile to copy its index to the clipboard (paste it straight into your Lua code).

Compile

usagi compile <path> packages a game for distribution. NOTE: compile will be improved soon to support cross-platform compilation. Right now it only supports compiling for the current operating system. Web builds won’t work unless you install Usagi from source and follow the Web Builds section below. By default it produces all artifacts in one export directory:

$ usagi compile examples/snake
[usagi] compiled snake-export/snake (3 file(s), 37125 bytes bundled)
[usagi] wrote snake-export/snake.usagi (3 file(s), 37125 bytes)
[usagi] wrote snake-export/web/ (3 game file(s), 37125 bundle bytes; runtime from embedded)
[usagi] export ready at snake-export/

$ tree snake-export
snake-export
├── snake             # native fused executable (./snake to run)
├── snake.usagi       # portable bundle (usagi run snake.usagi)
└── web/              # zip and upload to itch.io
    ├── index.html
    ├── usagi.{js,wasm}
    └── game.usagi

Or pick one with --target {all,exe,bundle,web}:

$ usagi compile examples/snake --target bundle
$ usagi compile examples/snake --target web
$ usagi compile examples/snake --target exe

Notes:

Web Builds

Usagi compiles to wasm via emscripten so games can run in a browser. See docs/web-build.md for setup, the build/dev loop, debugging tips, and the (non-obvious) wasm exception ABI requirements.

Developing

Reference and Inspiration

(Un)license

Usagi’s source code is dedicated to the public domain. You can see the full details in UNLICENSE.