macOS, Apple Silicon
An arm64 Mach-O, so an M-series Mac. There is no Intel build and no Rosetta path — the dylib paths inside it are the arm64 Homebrew prefix.
A first-person maze written in Rill, with no C written anywhere —
no shim library, no generated bindings, no build script. The whole path from
main to the GPU and the sound card is extern
declarations and raw buffers.
Walk it, swing an axe at what is on the walls, and find the brass bolt turning on the floor. Your torch burns out in forty seconds, something in here is listening for you rather than watching, and there are bats. It runs until you close it.
A folder with five things in it. artvandal is 405 KB of native code and assets.rpak is 50 MB — the paintings, the font sheet, the models and every recording, deflated into one file. The other three are the libraries it links against, which now travel with it: there is nothing left to install. Nothing is written anywhere except a settings file beside it; delete the folder and it is gone. The voice that tells you where you are plays on the first run and is never heard again — that is the other thing the settings file holds.
sha256 b9a24a90f0bedae72134257441881121043c770193b59a4f89562b351f4673ac
There is nothing to install. Unzip it and double-click it — the rest of this is only what to do when Gatekeeper says no.
Keep the folder together: the program looks for assets.rpak
beside itself rather than in whatever directory you happen to be in,
so the two travel as a pair and Finder works as well as a shell does.
Homebrew used to be step one — glfw, openal-soft
and mpg123 were linked from /opt/homebrew by absolute
path, and a machine without them could not start the thing at all. They are in
the folder now, loaded from beside the binary. Verified: with the program
running, nothing it has open comes from /opt/homebrew.
The download arrives quarantined, and this binary is signed only ad-hoc, not notarized. The gentle way is to right-click it, choose Open, and say Open again. The other way:
unzip art-vandal-macos-arm64-20260814c.zip xattr -dr com.apple.quarantine ArtVandal open ArtVandal/artvandal
what it prints on the way up
GL 4.1 Metal - 90.5 assets: 39 entries, 133072 KB inflated from 50841 KB maze: 2376 vertices, two generated textures torches: 15 font: 95 glyphs, em 96 wall faces: 196, one painting every 9 paintings: 20 blood: 70 stains, 43 running down the wall, 53 dragged along the floor remains: 23 of them, 73506 vertices from 5 models bolt: one of them, at 5.5,9.5 walker at 5.5,1.5 — 40 cells, 40 seconds away, 298 poses, 10830 vertices each bats: 5, 7 poses, 32832 vertices each sound: 16 fires, error 0
The maze is carved fresh each time, so those numbers are that run's and not yours.
An arm64 Mach-O, so an M-series Mac. There is no Intel build and no Rosetta path — the dylib paths inside it are the arm64 Homebrew prefix.
glfw for the window and the event pump, openal-soft
for positioned sound, mpg123 to decode the recordings — all three
in the folder, loaded from beside the binary rather than from Homebrew.
No installer and no application support folder. The one thing it writes is
artvandal.settings, beside itself, holding what you left the menu
set to. The memory is the models: every pose is a whole copy of the mesh, and
there are 298 of the walker — a walk, an attack, a death and a flinch.
There is no run. What makes you quicker is the brass bolt turning on the floor somewhere in the maze — one to a maze, and it is worth a fifth for as long as that maze lasts.
| Key | What it does |
|---|---|
| W A S D | walk |
| mouse | look around — the arrow keys do the same |
| left button | swing the axe |
| space | jump |
| escape | stop the engine and open the menu: the volumes, the size of the window, a second page listing these keys, and a hand to work them with |
| f | fill the screen, and come back out of it |
| r | a new maze, and a new camera with it |
The axe was on space, which is where a jump goes; swinging is the thing your hand is already on the mouse for. A first aid box gives you back a tenth of your blood and there are six of them — standing on one you do not need leaves it where it is, because there is nowhere to put what is in it. The bolt goes out with an arc rather than a chime: half a second cut out of a ray gun, at your ear rather than out in the maze, because a chime would have been the one sound in here that came from a different game.
The dark is not empty any more. The fragment shader walks the ray in strides of a fixed length and asks, at each one, how thick the air is there, how much light falls on that piece of it, and how much of what it scatters survives the trip back. Fog on the floor, smoke under the boards, both drifting, both lit by whichever fire the light actually passes rather than by a number that only knows how far away you are. Walk towards a fire and the air around it is a warm body before the fire itself is.
Ten blows put it down, and it stays down: the fall plays once and then the pose stops moving, so what is on the floor is the last frame of it. Before this, killing it made it turn round and wander off — the code for after you die was doing duty for a thing that had just been killed.
A blow that does not kill it staggers it, and staggered it neither walks nor swings. That is the first thing the axe has ever bought you; it used to be half a stride of shove and nothing else. It sounds like something as well — a knife going through something on a board and a bone snapping, forty milliseconds apart, because there is no recording of an axe going into a body and there is a reason for that.
Forty seconds, and the amber bar in the bottom left is what is left of it. The fires on the walls are the only thing that lights it again, and you have to stand at one for a second and a half — walking past is not enough. Which costs something, because standing still is also the one thing that makes no noise.
Every sound carries a distance in cells: a footstep three and a half, a swing six, glass off a wall eleven. Inside that, the noise becomes the cell it walks to. Standing where the noise was and finding nothing, it searches. Swing an axe and something answers from the dark, from where it is standing rather than from where you are — so what comes back to you is a bearing.
assets.rpak are found beside the binary,
so move the folder rather than the file. Finder and the shell both work.To find out what a language with no C escape hatch does when you ask it for a game.
Every OpenGL call, every GLFW window, every OpenAL source and every frame
that libmpg123 hands back is an extern declaration in Rill and a
buffer laid out by hand. There is no binding generator in this project and no
.c file to compile alongside — the language reaches the system
libraries directly, or it does not reach them at all.
The brick, the flagstone and the boarded ceiling are not loaded. They are written pixel by pixel at startup, which is why the pack holds paintings and recordings and no wall.
The maze is depth-first search, the torchlight is per-fragment, and the thing coming for you is 298 poses of a 10,830-vertex model — every frame the clip was drawn at, across four clips — thirty-odd cells away when you start, and it does not know that yet. The five bats under the ceiling are seven poses apiece, and the pose is carried from frame to frame rather than read off the clock, so that none of them goes undrawn at any frame rate.