The engine's sample courtyard in daylight: animated figures, a stone turntable and long shadows.

Native to the Mac.
Nothing in between.

Swift 6 and Metal 4 all the way down — no runtime, no translation layer, no editor you are required to learn. Your game is a Swift package.

Start with the sample $swift run WindageSample
macOS 26 or later Swift 6.2 A Metal 4 GPU Ships sandboxed or not — your call

A game is a Swift file

The engine owns when. Your game owns what.

Pacing, frames in flight and the fixed timestep are the engine's. Your pipelines, your draws and your rules are yours — the line sits exactly where every renderer it came from stopped being the same.

import Metal
import WindageEngine

final class Game: GameLoop {
    var body = CharacterController(feet: .zero)
    var world = CharacterController.Surroundings()

    func beginFrame() {}

    func step(deltaTime: Float) {
        body.step(yaw: 0, input: MovementInput(forward: 1),
                  deltaTime: deltaTime, in: world)
    }

    func viewportSizeChanged(width: Int, height: Int) {}

    func encode(into encoder: MTL4RenderCommandEncoder,
                frameSlot: Int, interpolation: Float) {
        // your pipelines, your draws
    }
}

The whole contract: GameLoop. Everything above it is the engine's.

Shows its work

A game engine that shows its work.

Every feature was pulled out of a working game, and every claim on this page is a number you can reproduce on your own machine, with the check that holds it.

The same jump, however often the game steps

Apex of a 6.3 m/s jump, in meters. The height it should reach is 0.992.

Steps a secondStepped simplyWindage Engine
300.8900.992
600.9400.992
1200.9660.992
2400.9790.992

Stepped simply, a game at 60 misses a ledge one at 120 clears. In Windage Engine one course of walking, jumping, sliding and landing ends in the same place at all four rates.

The sample's ruins from above at dusk, lit by braziers, casting long shadows.

Found by drawing one frame

Every shadow was a frame stale.

The scene read shadow maps that the same command buffer was still writing. In play it looked right — last frame's maps were there — and nothing said otherwise.

Drawing a single frame into a file showed it: the first frame came out different every run. Two barriers later, four runs of that frame are the same bytes, and the frame is a check.

What's in it

What a native Mac game needs to ship, and was identical every time somebody wrote it.

Frame pacing

Frames in flight, a fixed timestep, a clock that keeps time with the window hidden.

FrameRenderer · FixedTimestep

Light and shadow

Cascaded sun shadows, and spot and point lights binned into clusters that each cast.

ShadowCascades · ClusteredLights

Characters

Skinned animation from USD, cross-fades, and things hung off joints.

SkeletonPose · TransformHierarchy

A body with feet

Stairs, lifts, turntables, being pushed — and the same at any step rate.

CharacterController

Levels

A versioned level format, and an editor you use inside your running game.

Level · LevelEditor

Sound, input, settings

Positional audio, keys and pads a player rebinds, settings that save.

AudioEngine · InputBindings

Many copies

Instanced drawing: a wood of trees or a field of debris in one draw per kind.

InstanceBatches · InstanceBuffer

Detail by distance

Simpler versions of every mesh, made when it loads; each copy draws the one its distance earns.

MeshDetail

What it deliberately isn't

Each of these is something the engine leaves to you, or to a game that proves it is needed.

No scene graph

Your game owns its objects. The engine owns when things happen.

No ECS

Structure your game the way your game wants to be structured.

No editor inside the engine

A level editor embeds in your game and edits its levels in place. A standalone editor is a separate product built on the engine, not part of it.

Not a runtime

Mac first. Another platform means a native port, not a wrapper.

How it's checked

On every change, before it's called done.

645

Tests

Each new one shown to fail with its feature switched off.

swift test
20

Scripted walks

The sample played by script, invisibly — one course of them at four step rates.

python3 Tools/walks.py
5

Recorded frames

Drawn with no window and compared pixel by pixel against the last good one.

python3 Tools/frames.py