onsoku advance is sonic advance, the game boy advance game from 2001, built again for a browser. there is no emulator in it. the game's rules are written again in javascript, and everything on the screen and in the speakers is read from the player's own copy of the game, on their machine, when they open the page. the first act, neo green hill zone act 1, plays whole, and the game was checked against the original a frame at a time. onsoku is japanese for the speed of sound.

1 act · 8,854 lines of javascript · 262 recordings matched · 0 bytes of the game in its repository · 2 days

  1. the player picks their ROM
  2. the extractor finds its tables
  3. the engine steps sixty times a second
  4. a 240 × 160 canvas, and the sound engine

it can be played at nahuelgenchi.github.io/onsoku-advance, with a copy of the game, and its code is on github. every frame in this article was drawn by it.1

nothing from the game

the repository holds no sprite, no tile, no note of music and no line of the original's code. the page asks for the game's ROM, the file a cartridge is copied into, reads it in the browser and never sends it anywhere. it can keep it on the device, so the next visit starts at once.

reading a ROM means knowing where things are in it. the tested copy, the american one, is known by its checksum, and its tables sit at fixed places: the sprites at 0x0D868C, the act's tilemaps at 0x4A6C40, its collision at 0x4A6DA4, the songs at 0x6D76AC. the formats come from SAT-R, the community's decompilation of the game, which names its functions and its data. none of its code or data is in the repository either.

another version of the game keeps the same tables in other places, so the extractor also looks for them by their shape. the tilemap table is the one where act 2 repeats act 1's background in each of the first five zones, so five pairs of its entries match. the collision table is the one whose first entry points at the same tiles and maps as the tilemaps' first two. the spin dash's speeds are nine numbers from 6.0 to 9.0, 0.375 apart.

neo green hill zone act 1 whole, a long strip: a flat beach on the left, a brown checkered hill falling to the right with two loops on it, then islands of rock and grass with waterfalls and loops, a dome, and level ground to the right
  1. sonic starts
  2. the first two loops
  3. the first grind rail
  4. the forced slide
  5. the half-pipe
the whole act, drawn from the ROM at a quarter of its size: 136 blocks across and 14 down, each 96 pixels square. it is fifty-four screens wide. opened large, it can be looked into.

what a pixel of ground is

the act is drawn in tiles of 8 × 8 pixels, and its ground is kept with the same tiles. each of the act's 982 tiles has a height for each of its eight columns and each of its eight rows, half a byte each, an angle, from 0 to 255 for a whole turn, and two bits of flags, one of which makes it a ledge that can be jumped through from below. a tile drawn mirrored is solid mirrored too. so a pixel of ground is found by asking which tile is drawn there, and which way round.

a loop needs one more thing. where it closes, the ground that leads into it crosses the ground that leads out, so the two halves of a loop are on different layers, and he collides with one layer at a time. thirty-one invisible boxes at the loops move him from one to the other: 11 to the front layer and 20 to the back.

the first loop of the act on its slope, drawn as the game draws it: brown checkered rock, grass on top, empty sky through the loop the same loop with its collision drawn over it: the left half of the loop in blue, the right half in red, the solid rock around it in purple
the first loop, and what is solid in it. red is solid on the front layer, blue on the back one, purple on both. the loop's two halves are on different layers.
sonic curled into a blue ball rolls down a checkered slope into a loop, up its right side, across the top upside down, down its left side and out along the slope
a second and a half of play, at the game's speed: sonic rolls down the slope, round the loop, and out. a frame of the strip is every sixth frame of the game.

sixty steps a second, in the original's order

the original runs everything as tasks in one list, sorted by a number each task carries, and so does onsoku advance, with the same numbers. the order is not a detail. the objects run before sonic, so a spring or an enemy tests where he was at the end of the frame before, and a frame later he is where the original would have him.

a frame of act 1, in order
prioritywhat runs
0x0900the ends of the grind rails
0x0F00the camera
0x1000the forced slide
0x2000the rings, what spawns, the two palette animations, and every object and enemy in the order it was made
0x2001rings sonic has dropped, and the sparks off a rail
0x2100the title card, while it plays
0x2180the rings, score, time and lives in the corners
0x3000sonic
0x4000the music
0xFF00the stage's clock

sonic's own step follows the original's player code, and its numbers are the original's. a spin dash starts from a crouch with a press of jump. each further press adds 2.0 to a charge, which loses a thirty-second of itself every frame, and letting go of down looks the speed up in a table in the ROM.

sonic crouches on the beach, curls into a ball spinning in place in a cloud of dust, then shoots off to the right as a ball and rolls up a hill
  1. he crouches
  2. jump: the spin dash starts
  3. a rev
  4. down let go, at 8.25 pixels a frame
a spin dash with three revs, at the game's speed. a frame of the strip is every fourth frame of the game.

the table has nine speeds, and the original never checks the charge against it. revved fast enough, the charge reads past the ninth into the table stored after it, which runs from 8.25 to 11.25. so a quick player can leave faster than any speed the first table holds, or slower than the one before it. onsoku advance reads both tables, as the game does, and stops at the end of the second, where the original goes on into bytes that are not speeds at all.

checked a frame at a time

nothing was judged by eye. the reference is mGBA 0.10.5, an emulator, running the original ROM with no window and writing out the game's memory after every frame: sonic, the camera, the rings, every task. each recording is a short piece of play, the same inputs given to both, and the engine passes when its state is the original's on every frame.

recordings the engine matches on every frame, by what they test
recordings
wall poles40
the ramp, the half-pipe and the slide40
turning sprites38
grind rails25
item boxes21
waterfalls and ledges16
platforms14
boosters14
enemies13
checkpoints and the restart12
the title card12
getting hurt and dying10
boulders7
262 in all. the bar picked out is the one compared picture against picture, as the next part tells.

a comparison that strict brings the original's quirks along with its rules, and they are kept. the rings are searched for by regions of 256 pixels, and the search stops 16 pixels short of the bottom of sonic's box, so a ring just inside the next region down is missed for a frame. a thing whose spawn point is on the very first pixel of a region appears a frame late. the forced slide, once the camera has been near it, never leaves the list of tasks: it may only go on a frame with nobody in it, and the second player's empty place always counts as someone.

the title card is checked as pictures. it slides in over the act while the screen fades up from black, and the engine's frames match mGBA's screenshots pixel for pixel on every frame of it, in twelve runs, ten of them pressing a button at different moments of it. to get there the fade had to darken colours the way the emulator does, from its eight bits a channel, rather than from the five the console has.

the title card: a blue zigzag band down the left with sonic the hedgehog written on it, a white bar across the bottom with neo green hill zone and act 1, over the act fading up from black with sonic standing on the beach
  1. the bar and the zone's name slide in
  2. the name stands, and act 1 rises
  3. the fade is done
the card's first two seconds, at the game's speed, a frame of the strip every sixth frame of the game. it lets sonic go on its 226th frame, or eleven frames after a press of a button between its 36th and its 214th.

the music is checked the same way. sonic advance plays its songs on nintendo's standard sound engine, which many game boy advance games use, and onsoku advance carries a port of it that reads the songs, voices and samples from the ROM. it mixes 176 samples a frame, at 10,512 a second, as the game sets it to. loaded with the original's state and run one frame, it matches mGBA's byte for byte in 3,657 of 3,659 frames of the act's music. the two that do not are frames where the game itself starts a song or changes its pitch or volume, outside the engine. the four channels the console makes in hardware, two squares, a wave and noise, are emulated, and match in pitch, level and envelope, but not sample for sample: where a wave starts depends on the exact cycle the game writes to them, which nothing here counts.

turning a sprite the way the console does

sonic's walking and running frames tilt with the ground. the console does that with hardware: a sprite gets a matrix, and every pixel on the screen is looked up in the sprite through it, nearest pixel, line by line. the first version of the renderer turned his frames with the canvas instead, which smooths and places pixels its own way. his position was right, and the pictures were not.

now the renderer builds the matrix from his angle as the game's TransformSprite does, moves him so his anchor stays put, and samples each piece of the sprite the way mGBA's renderer does, with its rounding and the places it skips. in the run that dies on spikes and starts the act again, 603 frames differed from the emulator's screenshots around sonic. afterwards none did. the other 37 recordings of that bar, three of them whole loops, turn him through the whole circle facing left, and through the angles act 1 gives him facing right.

two extras, off by default

the game is checked against the original screen, so anything added has to leave it alone, and both extras are switched off until a player asks for them.

the first is a widescreen view, up to 368 × 208, which shows more of the act around the original screen. only the drawing changes. the camera, and the tests of what is near enough to spawn or far enough to vanish, still work on the 240 × 160 screen, so the game plays exactly as it does without it. the price is honest: a shot from an enemy still disappears at the edge of the original screen, which is now inside the view.

the second is not sonic advance's at all. it is the boost from sonic rush, the 2005 game for the nintendo DS, its rules read from that game and checked against melonDS, an emulator of the DS. it needs the player's copy of sonic rush too, read the same way, and nothing of it is in the repository. held, it sends sonic at 12 pixels a frame, the camera falls behind him for eight frames and then runs up to 88 pixels ahead, and enemies in a box 64 pixels square around him burst. the trail behind him is drawn as the DS draws its own, as translucent 3D polygons, and the sound is played by a port of the DS's sound engine. switched off, every frame is what it was without it, which was checked over about 140,000 frames.

sonic walking slowly to the right along the beach, past a palm tree
sonic bursting away in a flash of blue light, then running fast over the hills with a long blue trail behind him and a pale aura around him, the camera falling behind and then running ahead
the same inputs, from the same frame, at the game's speed. a the boost switched off: the button does nothing, and sonic walks on. b switched on: from the third frame of the strip he is boosting.

two days

the repository was made just after midnight on 23 october 2026, and the boost went in late on the 24th. everything in between came one change at a time, each with its own issue and its own pull request.

  1. the extractor: sprites, palettes and tiles out of the ROM, in the browser
  2. sonic on a platform, with the original physics and camera
  3. the whole act, with its collision, slopes and loops
  4. the sound engine
  5. enemies and spikes, getting hurt and dying
  6. item boxes, platforms, boulders, waterfalls and boosters
  7. checkpoints, wall poles and grind rails
  8. the ramp, the half-pipe and the slide, then the title card
  9. sprites turned the way the console does
  10. the widescreen view, then a controller
  11. sonic rush's boost

what is left is most of the game. the act has no end yet, and losing the last life does not end it. the other zones, the bosses and the special stages are not there yet, and the boost has no gauge to run down. the other way round from this, a real game run inside another by an emulator, is inside kirby super star ultra.

notes

  1. the frames were drawn by the game itself, in chrome with no window, from a copy of the tested american version: a page loads its modules, steps the engine one frame at a time with scripted presses of the buttons, and copies the canvas after each. the strips are every fourth or sixth frame, enlarged to twice the console's pixels, and the act is drawn from the same copy at a quarter of its size. the comparison of turned sprites runs the same presses on the game as it was before the change. the counts of recordings come from the game's notes on the ROM and from its pull requests.