Shimmer Engine Wiki
Everything about making Game Boy Advance games in Shimmer Engine: drawing maps and sprites, scripting with event blocks, music, cutscenes, and building a real .gba ROM.
What is Shimmer Engine?
Shimmer Engine is a game maker for the Game Boy Advance, inspired by GB Studio. You draw your maps, place characters, script what happens with event blocks, write music, and press Build ROM. The result is a real .gba file that runs in emulators like mGBA, and should run on real hardware too.
Everything a build needs is included in the installer. There's no compiler, Python or devkit to set up.
Shimmer Engine is an early alpha. Things change from version to version, and your feedback decides what gets fixed and added first. Report problems on r/ShimmerEngine.
Coming from GB Studio? Most ideas carry straight over: scenes, actors, triggers, event scripts, GB Studio fonts and .uge songs. The big differences come from the hardware: far more colors, bigger sprites, real background layers and 16 actors per scene.
Installing
| System | Download | Notes |
|---|---|---|
| Windows | Shimmer-Engine-Setup-<version>.exe | Run it and Shimmer opens. Windows may ask you to confirm, since the installer isn't code-signed yet. |
| Linux | .deb or .AppImage | The .deb installs with a double-click. On Ubuntu 22.04-based distros (Zorin 17, Mint 21), the AppImage first needs sudo apt install libfuse2. Runs natively, so no Wine needed. |
| Mac | .dmg | Apple Silicon (M1 and newer). The first time, right-click the app and choose Open. |
To play your game you also need a GBA emulator. mGBA is a good choice. Set it as the program that opens .gba files, or pick it in File → Emulator for Play.
Your first game
- New Project. On the start screen, type a name, press New Project and choose a folder. You get a project with one scene, a background and the default player.
- Look around the scene. The middle is the scene canvas. The left sidebar lists scenes and project data; the right panel shows whatever you've selected.
- Add an actor. Pick the actor tool in the canvas toolbar and click on the map. Give it a name in the right panel.
- Make it talk. With the actor selected, open its On Interact tab, press + Add Event and choose Display Text. Type a line.
- Paint some walls. Turn on the collision layer and paint solid tiles so the player can't walk through buildings.
- Press Play. Shimmer builds the ROM and opens it in your emulator. Walk up to the actor and press A.
From here: draw your own backgrounds and sprites in the Art Editor, add more scenes and connect them with doors, and pick a game mode if you're not making a top-down game.
Editor tour
The dropdown in the top-left switches between the editor's sections:
Game World
Scenes, actors, triggers and their scripts. Where most of the game gets made.
Sprites
Turn sprite sheets into animated characters: frames, states, directions and collision boxes.
Backgrounds
Your background images, with the numbers the GBA cares about: colors, tiles and size.
Art Editor
A built-in pixel art editor for every image in your project.
Music
A piano-roll editor for .uge songs, plus MIDI import.
Cutscenes
Turn a video into a full-screen GBA cutscene with sound.
Settings
Engine settings for each game mode, dialogue fonts and frames, controls and project options.
Menus
- File: New Project, Open Project, Save (Ctrl+S), Save As (copies the project to a new folder), Reload Assets (F5), Emulator for Play.
- View → Theme: Dark, Light, Midnight Blue or Classic Purple.
- The toolbar has Asset Reload, Open Folder, Undo/Redo, Play and Build ROM.
Every change saves automatically. Save just writes everything immediately and confirms it in the status bar.
Project files
A project is a normal folder, so you can keep it anywhere and edit its art in other programs too. Press Asset Reload after changing files outside Shimmer.
my_game/
project.json flags, items, variables, settings, custom scripts
scenes/ one .json file per scene (subfolders = folders in the list)
assets/
backgrounds/ scene background PNGs
sprites/ sprite sheet PNGs (+ .art.json layers from the Art Editor)
fonts/ frames/ ui/ dialogue fonts, box frames, menu cursor
music/ .uge songs
sounds/ .wav sound effects
cutscenes/ .cut video + .raw sound (made in the Cutscenes tab)
ROM/ your built .gba
Put a / in a scene's name, like Forest/Cave 1, to put it in a folder. Scripts, palettes, prefabs and sprites can be organised in folders the same way.
Scenes
A scene is one map, or one screen like a title or a menu. Each has a background image, collision, actors, triggers and scripts.
Backgrounds
Any PNG up to 2048×2048 pixels, sized in multiples of 8. Maps bigger than 512×512 stream in as you walk. Every 8×8 tile can use 15 colors plus a shared backdrop color, from up to 15 palettes per scene, with up to 1024 unique tiles.
Painting the map
- Collision: solid, water, damage, and one-way tiles that block from the top, bottom, left or right only.
- Brushes: 8px, 16px, fill, magic (every matching tile) and area select with copy and paste.
- Tiles: stamp tiles from the background onto the map without touching the original image.
- Colors: assign palettes to tiles.
Layers and parallax
Each scene can have up to two extra background layers that scroll at their own speed, with optional drift for things like clouds. GB Studio-style parallax strips also work.
Things you place
- Actors: characters and objects with sprites and scripts. 16 per scene at most.
- Triggers: areas that run a script when the player walks in, and optionally when they leave.
- Doors: triggers that take the player to another scene.
- Notes: editor-only reminders on the map.
- Prefabs: saved actors and triggers you can place again.
Right-click an actor, trigger or note on the canvas to delete it. Hovering shows the tile position as X= and Y=; an actor's position is its top-left tile.
Game modes
Every scene has a type that decides how the player moves:
| Mode | What it plays like |
|---|---|
| Top Down 2D | Classic RPG movement. Free or grid-based (a full 8 or 16 px per step), with running and optional diagonals. |
| Platformer | Gravity, variable jump height, coyote time, jump buffering, double jumps, wall jumps and slides, ladders, one-way platforms, dashing, crouching, and moving platforms you can ride. |
| Adventure | Free 8-way movement with momentum, running, dashing and pushing. |
| Shoot 'Em Up | The screen scrolls by itself and the player stays on screen. |
| Point and Click | The player is a cursor; hover over things and press A. |
| Logo | No player at all, for title cards and splash screens. |
Each mode's settings are in Settings → Engine; see the engine settings reference. A scene can override settings just for itself, and Set Engine Setting changes them mid-game.
Sprites with animation states named jump, fall, run, climb or dash are picked up by the modes automatically.
Actors and the player
Actors have a sprite, a movement and animation speed, a collision group, and four scripts:
- On Init: runs when the scene starts.
- On Update: loops in the background every frame.
- On Interact: runs when the player presses A on it.
- On Hit: runs when a projectile in its group hits it.
Click the player's start position to give the player its own On Init, On Update and On Hit scripts, animation speed and collisions. Every actor event can target the Player, and Self means "this actor" inside its own scripts.
Actors can be pinned to the screen for HUD elements, and in platformer scenes an actor can be a platform you stand on and ride.
Event scripting
Scripts are lists of event blocks that run top to bottom. Press + Add Event and search, or browse by category. Drag blocks to reorder them, and hold Alt or Ctrl while dropping to copy instead.
Where scripts live
- Scene On Init, On Player Hit and Timers.
- Actor and player scripts (see Actors).
- Trigger On Enter and On Leave.
- Custom scripts: reusable scripts you call from anywhere with Call Script.
- Attach Script To Button: runs whenever a button is pressed. It runs alongside play by default, so the player keeps moving. Tick Freeze player to stop everything until it finishes.
Data
| Kind | Holds | Limit |
|---|---|---|
| Flags | On/off switches | 32 |
| Items | Inventory, have or don't have | 32 |
| Variables | Whole numbers from −32768 to 32767 | 256 |
| Constants | Named fixed numbers for expressions | — |
Flags, items and variables are what the three save slots store.
Handy tools
- Disable event: right-click a block to switch it off without deleting it. Blocks with an Else branch can also Disable else.
- Expressions: If Expression and Set Variable To Expression take formulas using variables, flags, items, actors and math.
- Background scripts: up to 8 scripts can run at once. Dialogue boxes wait their turn.
- Pause the world during dialogue (an engine setting) freezes everything else while a box is open.
The full list is in the events reference.
Dialogue
Display Text shows a dialogue box. A new line starts a new page, and long text carries on to the next page automatically. The preview under the text shows exactly what the game will show.
Box options
- Position: bottom, middle, top, or custom (X, Y and width in tiles; the screen is 30×20 tiles).
- Rows: 1 to 4 lines of text.
- Frame: turn it off for just the text, handy for one-line HUD text.
Text codes
| Code | Does |
|---|---|
{score} | Shows a variable's value. |
!F:fontname! | Switches font from here on. |
!C:#ff4040! | Colors the text from here on. !C! goes back to the font's own color. |
!S2! | Text speed in frames per letter. !S0! shows text instantly. |
The Insert bar under the text box adds these for you.
Fonts and frames
Fonts use GB Studio's PNG format, so GB Studio fonts work as they are. There are 15 built-in fonts too. Fonts, frames and text colors share one 15-color dialogue palette.
Sprites
- Sprites can be almost any size up to 256×256; the engine packs them into the GBA's hardware sprites for you.
- Each sprite can use 15 colors plus transparency.
- Animation states for standing, walking and custom animations, in four directions. Flip right to make left saves drawing.
- Sheet slicing for GB Studio-style and RPG Maker-style sheets.
- A live preview of the character walking, and a meter for how much sprite memory each frame uses.
- Onion skin, frame copy and paste, mirroring and drag-to-reorder.
Art Editor
A pixel art editor for your project's images. Pick an image on the left, or make one with + New. Saving writes the PNG straight into your project.
Tools
| Tool | Key | Notes |
|---|---|---|
| Pencil | B | Brush size, dither and pixel-perfect options. Shift+click draws a line from the last point. |
| Eraser | E | Erases to transparent. |
| Fill | G | Connected area, or every pixel of that color with Global. |
| Line / Rectangle / Ellipse | L U O | Shift for 45° lines, squares and circles. |
| Gradient | D | Dithered, linear or radial, using only the colors you pick. |
| Shading | S | Left click lightens, right click darkens, stepping through the image's own colors. |
| Stamp | K | Draws a custom brush made with Image → Brush from selection. |
| Color picker | I | Or Alt+click with any drawing tool. |
| Select / Move | M V | Copy, cut, paste, flip, nudge with arrow keys. |
| Hand | H | Or hold Space and drag. |
Layers and animation
Layers can be hidden, locked, faded, reordered and merged; the game gets them flattened into one image. For sprite sheets, set a frame size in the Frames panel to get a timeline, a playing preview and onion skin.
GBA helpers
- The palette bar warns when a sprite goes over 15 colors, or a background tile over 15 plus the backdrop.
- Image → Reduce colors and Snap to GBA colors fix that for you.
- Tile mode (T) repeats the image around itself for seamless tiles. # shows the 8×8 tile grid.
Music and sound
- Music uses
.ugesongs, the same format as hUGETracker and GB Studio, played on the GBA's classic sound channels. - The Music section is a piano-roll editor with instruments, waves and effects.
- MIDI import turns a MIDI file into a playable song.
- Sound effects are WAV files in
assets/sounds, played on the GBA's two digital sound channels at 16384 Hz. Built-in sounds cover jumping, landing and more.
Cutscenes
Turn a video into a full-screen GBA cutscene with sound, for intros and story moments.
- Open Cutscenes and press + Import video. MP4 (H.264) and WebM work best.
- Trim it with Start and End, and pick a size, frame rate, fit and quality. The dialog estimates the size before converting.
- Press Convert, then watch it in the preview player. It plays exactly as the GBA will show it.
- Add a Play Cutscene event where it should play. A or START can skip it, and the scene comes back as it was afterwards.
| Size | Looks | Space |
|---|---|---|
| Full screen 240×160 | Sharpest | Biggest |
| Half 120×80, shown 2× | Chunkier pixels | About 4× smaller |
Sound takes about 16 KB per second. A cartridge holds 32 MB, so short cutscenes fit easily. Cartoon-style video compresses much better than live action.
Saving and the scene stack
- Save Data, Load Data and Clear Data use three save slots that hold flags, items, variables and where the player is.
- Store Current Scene remembers the scene and player position; Restore Previous Scene goes back. Good for menus and houses.
- Tick Remember everything for a real pause. Actors, their states, running scripts and timers all come back exactly as they were, and the scene's On Init doesn't run again.
Building and playing
- Play builds the game and opens it in your emulator. Pressing Play again closes the emulator it opened last, so windows don't pile up.
- Build ROM builds without playing. The ROM goes in your project's
ROMfolder. - If something's wrong, the build window says which scene and event. Copy Log copies it for sharing.
Real hardware
The ROM is a standard GBA ROM with a fixed header, so it should run on flash carts. Please report how it goes on yours.
GBA limits at a glance
| Thing | Limit | Why |
|---|---|---|
| Screen | 240×160 px | 30×20 tiles of 8×8. |
| Background size | 2048×2048 px | Bigger maps stream in as you walk. |
| Colors per background tile | 15 + backdrop | Each 8×8 tile uses one 16-color palette. |
| Background palettes | 15 | The 16th belongs to the dialogue box. |
| Unique background tiles | 1024 | Repeat tiles across big maps. |
| Colors per sprite | 15 + transparent | One sprite palette each. |
| Sprite size | up to 256×256 | Packed into hardware sprites. |
| Actors per scene | 16 | Plus the player. |
| Dialogue palette | 15 colors | Shared by all fonts, frames and text colors. |
| Running scripts | 1 + 8 | One main script plus background scripts. |
| Timers per scene | 8 | |
| Flags / items / variables | 32 / 32 / 256 | All saved with the game. |
| Save slots | 3 | |
| Cartridge | 32 MB | What everything has to fit in. |
How Shimmer Engine is built
Three parts work together. The editor writes plain files; the compiler turns them into C data; the engine is the GBA program that data is linked into.
| Part | Language | Job |
|---|---|---|
| Editor | TypeScript, React, Electron | Edits the project folder: JSON, PNG, UGE, WAV and cutscene files. |
| Compiler | Python | Checks the project and writes C source: scenes, scripts, sprites, fonts, songs, sounds. |
| Engine | C (devkitARM, libgba) | The runtime: game loop, game modes, script machine, graphics, sound, saves. |
The installer bundles a frozen copy of the compiler, the engine source and a trimmed devkitARM (GCC for the GBA's ARM7TDMI), which is why nothing else needs installing. The Linux version uses Arm's own GCC with devkitPro's GBA support files, because devkitPro's Linux compiler needs a newer glibc than Ubuntu 22.04-based distros have.
Project file format
Everything is plain JSON, so projects diff well in Git and can be edited by hand or generated by tools.
project.json
{
"name": "My Game",
"start_scene": "title",
"flags": ["met_elder", "door_open"], // up to 32
"items": ["Old Key"], // up to 32
"variables": ["gold", "hp"], // up to 256, int16
"constants": [{ "name": "MAX_HP", "value": 99 }],
"customScripts": [{ "id": "...", "name": "Heal", "script": [ ...events ] }],
"ui": { "font": "default", "frame": "default", "textSpeed": 1 },
"palettes": [ ... ], "prefabs": [ ... ],
"playerSprite": "player",
"spriteSheets": [ ...sprite definitions ],
"engine": { "pl_walk_vel": 2, ... } // engine settings by key
}
scenes/<name>.json
{
"name": "town",
"type": "topdown", // or platform, adventure, shmup, pointnclick, logo
"background": "../assets/backgrounds/town.png",
"music": "town",
"player_start": { "x": 14, "y": 9 }, // tiles
"collision": ["....####", "....#..."], // one string per tile row
"npcs": [{ "name": "elder", "x": 4, "y": 4, "sprite": "elder",
"on_interact": [ ...events ], "on_update": [ ... ] }],
"doors": [{ "x": 10, "y": 0, "width": 2, "height": 1,
"target_scene": "forest", "target_x": 3, "target_y": 18 }],
"on_init": [ ...events ],
"layers": [ ...parallax layers ],
"tile_overrides": { "3,4": [12, 0] }, // map tile ← background tile
"engine": { ... } // this scene's own settings
}
Collision characters
| Char | Meaning |
|---|---|
. | Walkable |
# | Solid |
~ | Water |
! | Damage |
^ v < > | One-way: solid only from the top, bottom, left or right edge |
H | Ladder (platformer) |
Events
An event is an object with a type and its options, and branches are nested lists:
{ "type": "if_flag", "flag": "met_elder",
"then": [ { "type": "text", "text": "Welcome back!" } ],
"else": [ { "type": "text", "text": "Hello, stranger.", "position": "top" } ] }
Any event can carry "__disabled": true, and an event with an Else can carry "__disableElse": true; the compiler skips them.
The build pipeline
- Compile the project.
build_project.pyvalidates everything and writesscenes_data.c,ui_data.c,uge_songs.c,sounds_data.c,mode_settings.hand the cutscene files into<project>/build/rom/data/. Errors name the scene, script and event. - Compile C. Each engine and data source is compiled with
arm-none-eabi-gcc -mthumb -mcpu=arm7tdmi -O2, in parallel, skipping files whose sources haven't changed. - Link with
gba.specsand libgba, objcopy to a raw ROM, and gbafix the header (title, checksum). - Copy the ROM to
<project>/ROM/.
Building from the command line
python3 compiler/build_rom.py <project folder> [--engine DIR] [--devkitpro DIR] [--out FILE]
It needs devkitARM, taken from $DEVKITPRO or /opt/devkitpro. The installed app runs the same tool, frozen as shimmer-build, from its resources/toolchain folder.
Big data
Cutscene video and sound go into the ROM through the assembler's .incbin rather than giant C arrays, so even multi-megabyte cutscenes build quickly.
The script machine
Event blocks compile to a flat list of instructions. Each one is a small struct:
typedef struct ScriptEvent {
uint8_t op; // what to do (SCRIPT_TEXT, SCRIPT_IF_FLAG, ...)
int16_t a, b, c, d; // operands: indices, numbers, jump targets
const void *ptr; // an expression or a sub-script
const char *str; // dialogue text
} ScriptEvent;
- Threads. One main script plus up to 8 background threads. Each keeps its own position and whatever it's waiting on (a text box, a wait, a moving actor, a camera pan, a button).
- Running. Every frame each thread runs instructions until one blocks, then picks up from there next frame. Branches and loops are jumps inside the list.
- Expressions compile to reverse-Polish arrays the engine evaluates on a small stack: variables, flags, items, actor positions, math and comparisons.
- The player stands still while the main script runs (talking to an actor, a trigger). Background threads, timers and button scripts run alongside play.
- Real pause. Store Current Scene with Remember everything copies every thread, timer and button script plus each actor's state, and puts them back when the scene is restored.
Graphics on the GBA
Video memory layout
| Area | Used for |
|---|---|
| BG char blocks 0-1 | Scene tiles (up to 1024, 4bpp) |
| BG screen blocks 28-31 | Scene map on BG0, up to 64×64 tiles |
| BG screen blocks 24-27 | Parallax layer maps on BG2 and BG3 |
| BG char block 2, screen block 23 | Dialogue box and text on BG1 |
| BG palette banks 0-14 / 15 | Scene colors / dialogue colors |
| OBJ VRAM (0x06010000) | Sprite tiles, streamed in per animation frame |
| OBJ palette banks | Assigned per scene; the player always gets bank 0 |
Big maps
Maps up to 64×64 tiles (512×512 px) sit in VRAM whole. Bigger ones, up to 256×256 tiles, stream: as the camera moves, the engine copies in the row or column that's about to appear.
Sprites
The GBA draws sprites from hardware objects (OBJs) of fixed sizes, 8×8 up to 64×64. The compiler covers each frame of a sprite with as few OBJs as it can, removes duplicate tiles, and makes "flip left" frames from flipped OBJs instead of new pixels. Each actor reserves OAM slots and VRAM for its largest frame and streams the current frame in.
Dialogue
Text isn't made of font tiles. It's drawn pixel by pixel into a RAM canvas the size of the box (variable-width fonts, colors and all) and copied to BG1 when it changes.
Cutscenes
Full-size cutscenes use BG0 with 256-color (8bpp) tiles; half-size ones use an affine BG2 scaled 2×. Each frame updates only the tiles that changed.
Sound on the GBA
- Music plays on the four classic PSG channels (two pulse, wave, noise) through a C port of hUGEDriver, so
.ugesongs sound as they do in GB Studio. The editor's player is a TypeScript port of the same driver feeding a PSG synth. - WAV sounds use the two Direct Sound channels. Samples are signed 8-bit mono at 16384 Hz, streamed from ROM by DMA 1 and 2 and clocked by timer 0, so they cost almost no CPU. They mix over the music.
- Cutscene sound is a WAV on channel A, started with the first video frame; video timing comes from the same hardware clock, so they stay in sync.
Save data
Saves go to the cartridge's battery-backed SRAM: 32 KB at 0x0E000000, read and written a byte at a time. Each of the 3 slots gets a 1 KB stripe holding:
typedef struct {
uint32_t magic; // marks a slot as written
uint32_t flags; // one bit per flag
uint32_t inventory; // one bit per item
uint8_t scene_index;
int16_t player_x, player_y;
uint8_t player_direction;
int16_t variables[256];
uint8_t checksum;
} SaveData;
The ROM carries the SRAM_V113 marker so emulators and flash carts know which save type to give it.
Cutscene file format (.cut)
Little-endian. The editor writes it; engine/source/cutscene.c plays it.
| Offset | Contents |
|---|---|
| 0 | "SHCV" |
| 4 | u16 version (1) |
| 6 | u8 mode: 0 = full 240×160, 1 = half 120×80 shown 2× |
| 7 | u8 frames per second |
| 8 | u16 frame count |
| 10 | u8 tiles across, u8 tiles down |
| 12 | u16 palette[256], GBA BGR555 |
| 524 | u32 frame offsets[count + 1] |
| … | Frames: GBA LZ77 streams, 4-byte aligned |
Unpacked, a frame is a bitmap with one bit per tile (rounded up to 4 bytes) followed by 64 bytes of 8bpp pixels for each tile whose bit is set. Frame 0 sets every tile. The BIOS LZ77UnCompWram call unpacks each frame into work RAM before the changed tiles are copied to VRAM. The .raw file beside it is the sound: signed 8-bit mono at 16384 Hz.
Building from source
cd editor npm ci npm run dev # editor with hot reload npm run dist # package an installer for this system
Development builds of ROMs run compiler/build_rom.py through bash (WSL on Windows) with your own devkitARM. The GitHub Actions workflow builds the Windows, Linux and Mac installers, and test-builds a ROM with each bundled toolchain.
The source layout is described in ARCHITECTURE.md in the repository.
Events reference
Every event block, generated from the editor's own list. Search above to filter, or pick a category.
Engine settings reference
From Settings → Engine. Settings marked GBA are extras GB Studio doesn't have.
Troubleshooting
Play does nothing, or says it can't open the ROM
Install an emulator like mGBA and set it to open .gba files, or choose it in File → Emulator for Play.
"Too many colors"
Open the image in the Art Editor. The palette bar shows how far over you are; use Image → Reduce colors.
An actor ends up a tile lower than expected
Positions are the actor's top-left tile, but a 16×16 actor covers four tiles. Use the Pick button, which shows the whole actor. In a platformer, gravity also pulls a player placed in mid-air down.
My button script freezes the player
Untick Freeze player while it runs on Attach Script To Button. Unticked, the script runs alongside the game.
A video won't import
Use MP4 (H.264) or WebM. Other formats can be converted with a free video converter first.
The Linux AppImage won't start
Run sudo apt install libfuse2, or use the .deb instead.