Current version: 0.8.0
MiniPixels is a pixel-oriented 2D game engine prototype for MiniLang. It uses MiniLang Compiler 1.2.4 or newer and builds native Windows x64 PE and Linux x64 ELF executables.
MiniPixels focuses on a small but working 2D engine slice: native Win32 and X11 windows, a fixed logical framebuffer, OpenGL/WGL, GDI and XImage presentation, configurable keyboard/mouse actions, sprites and rotated render targets, cached asset packs with general PNG decoding, scene stacks, swept tile collision, bitmap text, multi-voice PCM audio through waveOut or ALSA, headless tests, and example projects.
Browse the committed MiniDoc API reference, or
open docs/api/html/index.html locally for the searchable offline site. Source
files use //! file documentation and /// declaration comments with
structured @param and @returns contracts.
Regenerate both formats, or validate the source documentation without writing output:
pwsh .\tools\generate_minidoc.ps1
pwsh .\tools\generate_minidoc.ps1 -CheckThe strict minidoc.toml configuration treats documentation
diagnostics as failures.
- Windows x64 or Linux x64 with glibc, X11 (
libX11.so.6) and ALSA (libasound.so.2) - MiniLang Compiler 1.2.4 or newer in a sibling
MiniLangCompilerPycheckout, or a Python/self-hosted compiler path passed with--compiler - Python 3.11 or newer for the MiniPixels CLI and compiler project cache
Expected sibling layout during local development:
MiniLangCompilerPy/
MiniPixels/
Build and run the Moving Sprite example:
cd MiniPixels
python tools\minipixels.py run examples\moving-sprite\minipixels.json --compiler ..\MiniLangCompilerPy\mlc_win64.pyBuild without running:
python tools\minipixels.py build examples\moving-sprite\minipixels.json --compiler ..\MiniLangCompilerPy\mlc_win64.pyOn Linux, the CLI selects linux-x64 automatically. Cross-compile the same ELF output from Windows by passing the target explicitly:
python tools\minipixels.py build examples\moving-sprite\minipixels.json --compiler ..\MiniLangCompilerPy\mlc_win64.py --target linux-x64python3 tools/minipixels.py run examples/moving-sprite/minipixels.json --compiler ../MiniLangCompilerPy/mlc_win64.py
python3 tests/run_tests.py --target linux-x64
python3 tools/build_examples.py --target linux-x64The self-hosted 1.2.4 compiler is accepted directly as well, for example --compiler ..\MiniLangCompilerML\build\mlc_win64.exe.
Build the native MiniLang CLI:
python ..\MiniLangCompilerPy\mlc_win64.py tools\minipixels_cli.ml build\tools\minipixels.exe -I src -I ..\MiniLangCompilerPy
build\tools\minipixels.exe info
build\tools\minipixels.exe validate examples\moving-sprite\minipixels.json
build\tools\minipixels.exe info examples\moving-sprite\minipixels.json
build\tools\minipixels.exe generate examples\pixel-effects\minipixels.json
build\tools\minipixels.exe generate examples\jump-and-run\minipixels.json examples\jump-and-run\build\generated\generated
build\tools\minipixels.exe new my-game platformerOn Linux, add --target linux-x64 to the compiler command and omit the .exe suffix:
python3 ../MiniLangCompilerPy/mlc_win64.py tools/minipixels_cli.ml build/tools/minipixels -I src -I ../MiniLangCompilerPy --target linux-x64
build/tools/minipixels infoThe native CLI provides info, doctor, validate, generate, and new. Native generate writes a deterministic assets.mpx, importable generated.assets and generated.levels modules, sheet/audio/file helpers, and imports either MiniPixels level JSON or Tiled/TMJ. Compiler launching and SDK packaging remain in the Python project driver.
Tooling split:
| Task | Native MiniLang CLI | Python CLI |
|---|---|---|
| Create a project | new |
new |
| Inspect/validate manifests | info, doctor, validate |
info, doctor, validate |
Generate generated.assets |
image/procedural/audio/file helpers backed by assets.mpx |
image/procedural/audio/file helpers backed by assets.mpx |
Generate generated.levels |
MiniPixels levels.json and Tiled JSON/TMJ |
MiniPixels levels.json and Tiled JSON/TMJ |
| Create runtime assets | deterministic assets.mpx |
deterministic assets.mpx plus build reports |
| Build/run/package | Not yet | build, run, package, pack |
Run tests:
python tests\run_tests.pyOptional window renderer smoke test:
python ..\MiniLangCompilerPy\mlc_win64.py tests\window_renderer_smoke.ml build\tests\window_renderer_smoke.exe -I src -I ..\MiniLangCompilerPy
build\tests\window_renderer_smoke.exeOptional renderer benchmark:
python ..\MiniLangCompilerPy\mlc_win64.py benchmarks\renderer_bench.ml build\benchmarks\renderer_bench.exe -I src -I ..\MiniLangCompilerPy
build\benchmarks\renderer_bench.exeBuild all examples:
python tools\build_examples.pyCreate the SDK bundle:
python tools\package_sdk.pyimport minipixels as mp
x = 40
y = 40
function update(game, dt)
global x, y
if game.input.left then x = x - (90 * dt) end if
if game.input.right then x = x + (90 * dt) end if
if game.input.up then y = y - (90 * dt) end if
if game.input.down then y = y + (90 * dt) end if
end function
function render(game, canvas)
canvas.clear(mp.rgb(20, 20, 30))
canvas.fillRect(x, y, 16, 16, mp.rgb(255, 128, 0))
end function
function main(args)
cfg = mp.createConfig("MiniPixels Game", 320, 180, 4)
mp.useGpuRenderer(cfg)
return mp.run(cfg, void, update, render, void)
end functioncreateConfig uses renderer = "auto" by default. On Windows that tries the OpenGL/WGL presenter first and falls back to GDI. Linux uses the X11/XImage CPU presenter; a requested GPU renderer reports opengl-unavailable-linux as its fallback reason. Use mp.useCpuRenderer(cfg) to request the native CPU path explicitly.
Presentation scaling can be selected per game:
mp.useStretchScale(cfg) # fill the whole window
mp.useFitScale(cfg) # keep aspect ratio
mp.useIntegerScale(cfg) # pixel-perfect integer scaling
mp.setSmoothing(cfg, false)game/
minipixels.json
src/
main.ml
assets/
player.png
Example project file:
{
"name": "moving-sprite",
"main": "src/main.ml",
"window": {
"title": "MiniPixels Moving Sprite",
"width": 320,
"height": 180,
"scale": 4
},
"assets": [
{
"id": "player",
"type": "image",
"path": "assets/player.png",
"sheet": {
"frameWidth": 32,
"frameHeight": 32,
"spacing": 0,
"margin": 0
}
},
{
"id": "jumpSound",
"type": "audio",
"path": "assets/audio/jump.wav"
}
]
}python tools\minipixels.py run examples\moving-sprite\minipixels.json --compiler ..\MiniLangCompilerPy\mlc_win64.pyDemonstrates a PNG sprite, keyboard movement, pixel snapping, FPS in the window title, and framebuffer scaling.
python tools\minipixels.py run examples\scrolling-world\minipixels.json --compiler ..\MiniLangCompilerPy\mlc_win64.pyDemonstrates tilemaps, camera scrolling, simple platform collision, world-edge clamping, parallax bands, and jump movement.
python tools\minipixels.py run examples\jump-and-run\minipixels.json --compiler ..\MiniLangCompilerPy\mlc_win64.pyDemonstrates a complete small platform game with a main menu, three levels, coins, enemies, stomp combat, exit gates, scrolling camera, sounds, animation, and compact runtime assets adapted from the GandalfHardcore 32x32 sidescroller pack.
python tools\minipixels.py run examples\pixel-effects\minipixels.json --compiler ..\MiniLangCompilerPy\mlc_win64.pyDemonstrates direct per-pixel framebuffer manipulation from MiniLang.
python tools\minipixels.py run examples\tiled-platformer\minipixels.json --compiler ..\MiniLangCompilerPy\mlc_win64.pyDemonstrates the shared Tiled JSON/TMJ importer with a solid tile layer and object-layer spawn, exit, coins, and enemy patrol data.
python tools\minipixels.py new MyGame
python tools\minipixels.py info examples\moving-sprite\minipixels.json
python tools\minipixels.py doctor examples\tiled-platformer\minipixels.json
python tools\minipixels.py validate examples\moving-sprite\minipixels.json
python tools\minipixels.py generate examples\moving-sprite\minipixels.json
python tools\minipixels.py pack examples\moving-sprite\minipixels.json
python tools\minipixels.py build examples\moving-sprite\minipixels.json --compiler ..\MiniLangCompilerPy\mlc_win64.py
python tools\minipixels.py run examples\moving-sprite\minipixels.json --compiler ..\MiniLangCompilerPy\mlc_win64.py
python tools\minipixels.py packageThe Python CLI validates project JSON, writes deterministic asset/level modules and assets.mpx, emits asset-report.json, and invokes the MiniLang compiler. The native MiniLang generator now covers the same runtime asset kinds and level formats. Generated audio helpers create memory-backed WAV clips, so example builds do not need loose sound files next to the executable.
Windowed Windows games built through tools\minipixels.py build or run use the GUI PE subsystem by default, so double-clicking the executable opens only the game window and no companion console. Linux builds are normal ELF executables. Use --headless for Windows console-subsystem builds that are meant to print test or tool output.
Builds use MiniLang's exact-hit incremental artifact cache by default. Use --no-incremental for a forced rebuild, --debug to enable MiniLang call profiling, --release to state the default non-instrumented mode explicitly, and --verbose to print the compiler invocation.
assets.mpx is MiniPixels' deterministic runtime asset container. It is intentionally simple: a fixed header, a compact entry table, and contiguous payload bytes.
All multi-byte integers are unsigned little-endian values.
offset size field
0 4 magic bytes: "MPX1"
4 4 entry count: u32
8 variable entry table
... variable payload bytes
Each entry table record is:
size field
2 asset id byte length: u16
N asset id as UTF-8 bytes, no terminator
1 kind: u8
1 flags: u8, currently 0
4 payload offset from start of file: u32
4 payload size in bytes: u32
Current kind values:
| Kind | Asset type | Payload |
|---|---|---|
1 |
image or procedural |
non-interlaced PNG bytes |
2 |
audio |
Original audio file bytes, usually WAV |
3 |
file |
Original file bytes |
The runtime decodes stored, fixed, and dynamic Deflate streams, PNG filters 0 through 4, grayscale, RGB, indexed, grayscale-alpha, and RGBA data. Current decoding is non-interlaced; the Python packer still emits a deterministic 8-bit RGBA profile while the native packer can retain ordinary source PNG bytes. Audio and file assets are stored byte-for-byte.
Runtime APIs:
pack = mp.openAssetPack("assets.mpx")
img = mp.loadPngFromPack(pack, "player")
raw = mp.loadBytesFromPack(pack, "coin_sfx")
kind = mp.assetKindFromPack(pack, "coin_sfx")Text:
mp.drawText(canvas, "LEVEL 1", 8, 8, 1, mp.rgb(255, 255, 255))
mp.drawTextCentered(canvas, "READY", 72, 2, mp.rgb(255, 220, 80))Animation:
sheet = gen.sheet_player()
run = mp.animationFromSheet(sheet, 2, 4, 0.08)
run.play()
run.update(dt)
canvas.drawSprite(run.currentSprite(), x, y)Camera-space drawing:
mp.drawSpriteWorld(canvas, camera, playerSprite, player.x, player.y)
mp.fillRectWorld(canvas, camera, coin.x, coin.y, 4, 4, mp.rgb(255, 220, 80))Input and audio:
coin = mp.audioClip("assets\\audio\\coin.wav", "coin")
mixer = mp.audioMixer(4)
if mp.inputPressed(game.input, "jump") then
mixer.playSfx(coin)
end if
mixer.setSfxVolume(80)
mixer.playMusic(mp.musicClip("assets\\audio\\theme.wav", "theme"))
mixer.stopAll()Packed audio:
clip = gen.audio_coin_sfx()
mp.playAudio(game.audio, clip)minipixels: public facade and game loopminipixels.graphics.canvas: framebuffer, primitives, sprite drawingminipixels.graphics.font: 5x7 bitmap text helpersminipixels.graphics.sprite: images, sprites, sprite sheetsminipixels.assets.pack: MiniPixels.mpxasset container readerminipixels.assets.png: PNG decoder/encoder and screenshot supportminipixels.platform.windows: Win32 window, input, DIB rendererminipixels.platform.linux: X11 window, input, timing, and XImage rendererminipixels.input.input: buffered configurable keyboard/mouse actionsminipixels.world.camera: pixel-snapped 2D cameraminipixels.world.tilemap: tile rendering and AABB tile collisionsminipixels.animation.animation: frame-duration sprite animationsminipixels.assets.assets: generated asset registryminipixels.debug.debug: counters and framebuffer hash helpers
Implemented:
- Native Win32 and X11 windows
- Fixed logical resolution and resize stretch
- CPU RGBA8888 framebuffer with direct masked-DIB GDI presentation
- Nearest-neighbor GDI/XImage presentation and optional OpenGL/WGL presentation on Windows
- Buffered keyboard/mouse input only while the game window has focus
- High-resolution fixed updates, interpolation alpha, smoothed FPS/UPS, focus pause, and frame limiting
- Safe pixel operations and primitive drawing
- MiniPixels
.mpxgeneration in both project pipelines with indexed runtime caches - General non-interlaced PNG hot-loading plus deterministic screenshot encoding
- Native MiniLang generation for real image/procedural/audio/file assets and MiniPixels/Tiled levels
- Runtime asset packing for image/audio/file assets
- Cached spritesheets, animation, rotated sprites, render targets, and dirty-region GPU uploads
- Scene stack with enter/exit/pause/resume/update/render lifecycle
- Configurable action bindings, pointer coordinates/deltas/buttons, and wheel input
- Multi-voice PCM WAV mixer through waveOut/ALSA with bus/clip/channel volume, pan, and looping music
- Build-time SpriteSheet metadata and
asset-report.json - Build-time level JSON generation through
generated.levels - Camera, scrolling, parallax bands
- Tilemap culling, cached frames, growable layers, and swept collision
- Headless, framehash, PNG, PCM, lifecycle, and
std.testregression tests - Windows and Ubuntu GitHub Actions CI for tests and example builds
- SDK ZIP packaging with SHA256 checksum and release upload on
v*tags - Version file, changelog, and first-game guide
Not yet implemented:
- GPU-accelerated Linux presentation and additional Linux display protocols such as Wayland
- Full editor tooling
- Advanced physics or ECS
More detail is in docs/getting-started.md, docs/first-game.md, docs/manifest-reference.md, docs/examples.md, and docs/minipixels-architecture.md. Release notes are in CHANGELOG.md.