Oniq, a deterministic game engine in Rust

Oniq: a deterministic game engine in Rust

Shon Thomas
10 min read

A first look at Oniq, the Rust game engine behind Pixel Stroke and PolyConsole and the reason Chine and sigilbuzz exist: why it insists that every machine computes the same game down to the last bit, and three figures running it live in your browser.

oniqrustgame-enginewebgpuwebassemblymultiplayerdeterminism

The last two articles were about pieces of something bigger. Chine plays Spine animations and sigilbuzz shapes text, and I wrote both because a game engine I've been building needed them. This is the engine.

Everything in this frame is Oniq: Rapier 2D physics stepped at a fixed 60 ticks a second, the ECS, the sprite batcher, a night sky with stars, and a caption shaped by sigilbuzz and drawn as MSDF glyphs. Press and drag to stir it.

It's called Oniq. It's written in RustA programming language known for being fast and for catching whole classes of bugs before a program ever runs., it draws with WebGPUThe newer graphics and compute API for the web, designed around modern GPUs. Native programs use it too, through libraries such as wgpu., and the same code runs as a desktop app, in a browser and on Android, including an Nvidia ShieldAn Android TV set-top box from Nvidia, popular for streaming and for playing games on a television. plugged into a TV, and now on iPhone, iPad and Apple TV too. What's running above is the browser build: the engine compiled to WebAssemblyA compact binary format that browsers run at close to native speed. Languages such as Rust compile to it, so the same code can run natively and on the web., drawing into a canvas on this page.

Oniq isn't public yet. I plan to release it soon, and oniqengine.com is where that will happen. It already has three demos you can play, on a phone too. This article is a first look at what makes the engine different, and that comes down to one word.

Every machine agrees

Oniq is built for real-time multiplayer games, and there are two broad ways to keep players in sync. A server can run the game and keep sending everyone the state of the world, which gets expensive as the world grows. Or every player can run the whole game, and the only thing sent over the network is input: who pressed what, and on which tick. That second model is called LockstepA multiplayer model where every player runs the same simulation on the same inputs and must reach exactly the same result each step, so any difference between machines breaks the game.. It needs very little bandwidth, and replays are nearly free, since a recording of the inputs is a recording of the game.

The catch is that every machine has to compute exactly the same result from the same inputs, down to the last bit, on every tick of the match. Floating-point math doesn't promise that across machines: compilers fuse operations differently, math libraries round sin and cos differently, and a browser and a native build are separate programs. Each difference is tiny, and in a physics simulation tiny differences grow.

Oniq is designed around that problem, and you can watch it handle it:

Two copies of the same scene on Oniq's fixed-point physics. Every input goes to both on the same tick, three ticks after you click, like an input in a lockstep game, and each machine hashes its entire state after every tick. Nudge B and its next ball drops 2⁻³² m, about a quarter of a nanometre, to the right. The hashes split at once, and after a few bounces through the pegs the difference is easy to see. Resync rebuilds B from the start of the round and replays the same inputs until it lands on A's exact state.

The nudge is the smallest change the simulation can represent, and it's the same kind of difference that rounding on another machine would introduce. Without determinism, two players would be watching those two machines. That's a DesyncWhen two machines that should be running the same simulation stop agreeing, so players see different games., and in a lockstep game it ends the match.

The resync button is the other half of the story. Machine B throws its world away, starts over from tick zero and replays the round's inputs, and it lands on exactly the state A reached by playing live, hash for hash. That's the same property that makes replays, spectating and RollbackA multiplayer technique where each machine guesses the inputs it has not received yet, and when the real ones arrive, rewinds a few steps and simulates them again with the correct inputs. work.

How Oniq keeps the bits the same

The physics in that figure runs on Oniq's deterministic solver. Positions, velocities and angles are Fixed-pointNumbers stored as integers with an agreed number of fractional bits. Integer math gives the same bits on every processor, which floating point does not always do. numbers, Q32.32: 64-bit integers with 32 bits after the point. Trigonometry comes from lookup tables, and no floating-point value enters a step. Integer math gives the same bits on every processor, in every build, native or WebAssembly.

Rust
use oniq::math::fixed::FixedI32F32;
use oniq::physics::det::{Body, DetWorld, Shape, Vec2Fx};

let mut world = DetWorld::new();
world.gravity = Vec2Fx::from_f64(0.0, -9.81);

let mut ball = Body::dynamic(Vec2Fx::from_f64(-1.0, 5.0), FixedI32F32::from_num(1));
ball.set_circle_inertia(FixedI32F32::from_num(1), FixedI32F32::from_num(0.5));
let handle = world.add_body(ball);
world.add_collider(handle, Shape::Circle { radius: FixedI32F32::from_num(0.5) });

// Every tick: the same inputs, the same step, the same bits.
world.step(FixedI32F32::from_num(1) / FixedI32F32::from_num(60));

The solver handles circles, boxes, convex polygons and joints, with a sequential-impulse design modeled on Erin Catto's box2d-lite. It has sensors, which report what passes through them without pushing back, for pressure plates and goal zones, and continuous collision detection, so a bullet that travels farther in one tick than a wall is thick stops at the wall instead of skipping past it. It also has a character controller: a game gives a player a velocity, and the solver walks it up slopes and onto low ledges, slides it along walls, carries it on moving platforms, lets it push crates, and says whether it's standing on the ground. For everything that doesn't need to match across machines, like debris, weather and the rain at the top of this page, Oniq also has RapierAn open-source 2D and 3D physics engine written in Rust., the fast floating-point physics engine, so a game can use both.

Physics is only part of it. A simulation also has to visit things in the same order everywhere. Oniq's Entity component system (ECS)A way to structure a game where each thing is an id with plain data components attached, and systems run over every entity that has the components they need. iterates entities in a stable order, never in the order a hash map happens to produce, and systems that depend on each other run in dependency order while the rest run in alphabetical order, so adding a system never shuffles the others. Lockstep then reduces to a loop like this one, from one of the engine's networking demos:

Rust
// Every player's input for this tick, sorted by player and sequence number.
let inputs = gather.take_tick(Tick::new(tick), Tick::new(tick)).expect("all inputs gathered for tick");
// The next world is a pure function of the inputs and the previous world.
step_world(&mut world, &inputs);

On top of lockstep, Oniq has rollback for games that can't wait on the slowest connection: each machine predicts inputs it hasn't received yet and, when the real ones arrive, rewinds up to eight ticks and simulates them again. Rollback is only possible because the rewind lands on exactly the state it left.

Checked on your machine

Oniq's tests include a determinism oracle. Eight small scenes, from two balls in a box to a four-link chain, a pair of bullets fired at a thin wall and three characters that walk, ride and jump, run for 30 ticks each, and every tick's hash is compared with hashes recorded natively: six of the scenes on an Apple Silicon Mac, the bullets and the characters on a Windows PC. My CI (continuous integration)Automated checks that build and test every proposed change before it is merged, so problems are caught before they ship. runs the oracle natively. Checking it in WebAssembly was still on my list, and this article turned out to be the place to do it:

ScenarioTicksHash at the last tick, hereRecorded nativelyMatch

This runs in your browser right now. It needs WebAssembly but no GPU: the eight scenes from Oniq's determinism test, compiled for wasm32 and compared hash by hash with the ones recorded natively.

When I first ran it, every one of the 180 hashes from the first six scenes matched, which is the result the design promises and still a nice thing to see. The bullets came later, with continuous collision detection, and the characters after them, and their 60 hashes match in the browser too. If your table shows anything else, I'd like to know your browser and device.

What's in it

Determinism is the throughline, but Oniq is a whole engine:

Area What Oniq has
Platforms Windows, macOS and Linux, the browser, Android and Android TV, and iPhone, iPad and Apple TV
Rendering wgpu on WebGPU, Vulkan, Metal and Direct3D 12. Sprite batching, atlases, materials, point lights with 2D shadows, bloom, WGSL effects, and a procedural sky with stars, clouds, aurora and weather
Physics The fixed-point solver, with sensors, continuous collision detection and a character controller, for anything that must match across machines, and Rapier 2D or 3D for everything else, with surface materials, ray and shape queries, contact and sensor reports, and weld and pin joints
Networking Lockstep and rollback, snapshot interpolation, prediction and lag compensation, replays, and transports over QUIC, WebTransport and WebSocket, the last two from a browser as well
Text sigilbuzz for shaping and bidirectional text, Unicode line breaking with a keep-all mode for Korean, drawn as MSDF glyphs
Animation Spine 4.3 through Chine, plus bones and IK
Audio Native audio, and Web Audio in the browser
Input Keyboard, mouse, multitouch, gamepads and the Siri Remote, and every Apple Pencil sample with its pressure and tilt
Scripting and UI Sandboxed Rhai scripts, a dockable panel UI model, and a native renderer for it with touch-sized controls drawn over the scene
2D content Brushes, tilemaps, palettes and color math, image processing, selections and SVG export

The last row is there because of the first program built on Oniq that isn't a game.

One engine, several products

Pixel Stroke is a pixel-art editor I started the same weekend Oniq came back to life this spring. Its brushes, palettes, TilemapA level or background built from a grid of small reusable images called tiles., bones, text and scripting are Oniq features, and building it pushed the engine to be good at tools as well as games. Its native app for the iPad is what gave Oniq Pencil input and its own interface renderer. It gets its own article soon.

PolyConsole is an Emulator frontendThe program you see and use: it shows your games, takes your controller, and hands the emulation itself to an emulator core. for the TV, and it runs on the same engine on an Nvidia Shield, and on Apple TV next. Oniq never learns what an emulator is: it offers a way to put someone else's frames on screen, and PolyConsoleA game platform I'm building, with apps on Android and an online social service behind them. does the rest. That one gets an article too.

And there's Sqrly, the multiplayer game Oniq was started for. It's in development, and it's a story for another day.

Three demos to play

oniqengine.com has three demos running the engine in the browser, and none of them is a video:

  • Makeshift is a small physics platformer. Its hero can barely hop, so you conjure crates, planks, balls and springs, then stack, carry, nail and bounce your way to a beacon. Ice slides, moss grips and jelly bounces. One nail makes a hinge, and a second holds it rigid. It has ten levels, most of them climbing, and a sandbox, and on a touch screen you drag what you build straight into the scene. There isn't one image or sound file in it: every sprite is painted and every sound synthesized when the game loads. Its tests play each level with scripted input to prove it can be beaten, and the same code runs as a desktop app.
  • Qubit is that hero on its own, a hover robot rigged in Spine 4.3 and run by Chine: 47 bones, Inverse kinematics (IK)Solving a chain of bones backward from a target: you say where the end should be, and the solver works out how each joint bends to reach it. for its head and arms, and nine physics constraints that swing its antenna, scarf and thruster flame. It watches your pointer, reaches for it, and swings when you carry it. Spine's sample rigs are licensed for evaluation only, so Qubit's art and rig are generated from Rust source.
  • The lantern playground pours 64 bodies down two ramps and through fifteen posts. Six of them are lanterns carrying lights, every post casts a soft shadow, and your pointer carries a light of its own.

All three play in a phone or tablet browser, with touch controls, since that's where I expect most people will meet Oniq first.

The figures on this page run the same engine as those demos. Since the browser host landed, the engine ticks at the start of each frame on a clock snapped to the display's refresh, which takes a frame of LatencyThe delay between asking for something and getting it back. out of the browser build, and it can draw between ticks, so a 120 or 144 Hz screen moves smoothly. On the physics demo the spread of frame times fell from 1.42 ms (millisecond)A thousandth of a second. to under half a millisecond, and every steady frame now runs exactly one tick. Colors render exactly as they're written, and the demos' browser builds use WebAssembly SIMD.

How it got here

The first CommitA saved snapshot of changes in a repository, identified by a unique hash, so any past state of the code can be named and found again. is from October 20, 2025, and the first week brought an ECS, a renderer and text. Then the project sat through the winter. In April I came back to it with a clearer idea of what it was for, and from there it moved fast: scripting moved from Lua to RhaiA small scripting language designed to be embedded in Rust programs. It runs anywhere Rust does, WebAssembly included. so it could run in a browser, sigilbuzz replaced the text stack, lockstep, rollback and the fixed-point solver landed by the end of May, Android and the Shield in early June, and Spine through Chine right after. The browser host that runs the figures on this page landed in early October, and the three demos, phone support and the new frame pacing followed within days. Days later came iPhone, iPad and Apple TV, where the remote's Menu button, game controllers and audio all had to be taught to an engine stack that had never heard of tvOSThe operating system on Apple TV, a close relative of the one on the iPhone and iPad..

Today the engine alone is about 170,000 lines of Rust, counting comments and blank lines, and the RepositoryA project's files together with their full history of changes, usually kept with git and hosted on a service such as GitHub. has more than 3,500 tests across more than 720 commits.

What it doesn't do yet

  • 3D is a foundation: perspective cameras, a 3D skybox and Rapier 3D. It isn't a full 3D renderer.
  • There's no visual editor. Oniq is code first.
  • The iPhone, iPad and Apple TV builds have only run in Apple's simulators so far. Real hardware is the next step for them.

Current state

Oniq isn't public yet, and I plan to release it soon. Until then, oniqengine.com has the three demos above, and it's where the release will be announced.

The figures on this page are built from the same engine, pinned to one commit and compiled to WebAssembly: about 3 MB (megabyte)About a million bytes. A typical photo from a phone is a few megabytes., under a megabyte compressed, fetched once, when the first figure comes into view. Each scene runs in a frame of its own, and if your browser doesn't offer WebGPU you'll see a still from it and a note.

If you're building a multiplayer game and determinism is the part that keeps you up at night, I'd like to hear from you. That's the problem Oniq was made for.