Simple 3D software rasterizer and helping code for Playdate console. The same code can also be built for PC. Everything is in pure C, there are no Lua bits (yet?).
"Dators" means "computer" in Latvian. I don't know Latvian, but I like the word.
src/main.c contains a simple "3D game", where Chocomel the dog (from Blender Studio DOG WALK game - project page, steam page) has to run along a 3D curve in space, jumping over or digging through obstacles.
Web build of the sample game: https://aras-p.github.io/playdators/ (1MB).
Playdate build of the game: https://aras-p.info/files/games/2025_playdators/PlayDators_Sample.pdx.zip (1MB) -- should work on Playdate device and the simulator on Windows.
![]() |
![]() |
![]() |
![]() |
All the meshes there are from the Dog Walk project, and as such they are not particularly optimized for either Playdate, nor for a graphics engine that has no textures. I applied Blender's Decimate modifier on them at 15-20% of original poly count, exported, and that's it.
The music and sounds are from the Dog Walk project as well, converted to IMA ADPCM in Audacity.
The game tends to render 500-1000 visible triangles as well as a bunch of triangle edges, at runs at 45-50 frames per second on the Playdate device.
Controls:
- Right: move Chocomel,
- Up: jump (can only do that while running),
- B: dig through an obstacle,
- Crank / mouse wheel: orbit the camera a bit.
- A: toggle rendering between simple dithering and blue-noise dithering.
src/render.h contains a tiny 3D rasterizer.
- Meshes are either created from code procedurally (
mesh_create) or loaded from a custom binary format (mesh_load_from_file). Meshes contain:- Vertex positions,
- A single mesh can have multiple "frames" of animation, where vertex positions can vary for each frame.
- Triangle index buffer (3 16-bit vertex indices per triangle),
- Optional triangle colors (1 byte per triangle),
- Optional edge flags, to indicate which edges of triangle should be drawn when drawing wireframe.
- Optional texture coordinates (though they are fairly useless currently).
- Vertex positions,
- A "3D scene" is composed of
MeshInstanceobjects, which point to the mesh as well as per-instance parameters:- 3D transform matrix,
- Color (single float),
- Animation frame index,
- Drawing flags:
- Solid: uses per-instance color, combined with mesh per-face color if present,
- Solit and lit: additionally modulates the color by a wrapped diffuse term between view-space lighting direction and view-space triangle normal,
- Wireframe: draw triangle edges. Per-mesh data can be present to indicate which edges to draw.
- Silhouette wireframe: draw edges of "silhouette" faces. Mesh face adjacency is computed on first use, then edges are drawn between neighboring faces where one face is visible and the other is not.
- Sorting bias: additional value to to view space distance while sorting mesh instances for painter's algorithm.
- Objects in the scene are sorted back-to-front and drawn in that order (with optional per-instance sorting fudge), and then triangles each mesh are sorted back-to-front and drawn in that order too. There is no depth buffer, so sorting artifacts are entirely possible!
- Everything is rendered using either fixed dithering patterns, or dithering through a premade blue noise texture.
- Coordinate conventions are like in Unity game engine (left handed, Y up): +X to the right, +Y up, +Z goes forward into the screen.
- A Blender export add-on is in
blender/addonsfolder. It can be used to export meshes into a file format understood bymesh_load_from_file.
In order for the code to work on both the Playdate as well as PC, there is platform abstaction under src/platform.h:
plat_file_functions for working with files, similar to C stdio.plat_time_functions for querying time.plat_sound_functions for loading IMA ADPCM compressed WAV files and playing them back, including looping and volume control. Multiple sounds can be played at once.plat_input_functions for querying buttons and crank (on PC maps to mouse scroll wheel) input.plat_mallocand related functions for memory allocation.
src/mathlib.h contains a tiny "math library" for what was needed in this code. Major parts:
float3struct andv3_functions for working with 3D vectors,xformstructxform_functions for working with 3D transforms (3x3 rotation matrix expressed as three coordinate axes, plus translation vector),XorShift32based random number generator,- Various math functions like
min,max,clamp,saturate,lerp,smoothstepand so on. - Float to half precision (FP16) conversion:
float_to_half,float_to_half_topbitsthat map to built-in Playdate CPU instruction.
The build is done with CMake, and I only ever tested it on Windows. There, just open the folder in Visual Studio (that's the only IDE I tested; and I only tested VS2022 version).
There will be several platforms in the platform dropdown: regular x64 is for PC build, platforms with (Playdate) are for Playdate device build, and (Playdate Sim) is for
Playdate simulator build.
Source folder is expected to have data files; some of these will be automatically processed by Playdate SDK tools while doing a build (e.g. PNG files will get converted to .pdi; WAV files
to .pda and so on).
Building for the web with Emscripten:
- Have Emscripten installed and the SDK activated on the command line. I have tested with Emscripten 4.0.16 (2025 Oct).
- Change current directory to this folder,
cmake --preset emscripten-release,cmake --build --preset em-release- Built files will be under
build/emscripten_releasefolder. - In order to test them locally in the browser, easiest to run a web server from that folder:
cd build\emscripten-releaseandpython -m http.server, then openlocalhost:8000in your browser.
Most of the code layout and structure is taken from Everybody Wants to Crank the World demo (2024) and Surface-Stable Fractal Dithering on Playdate (2025).
Everything I wrote myself is Unlicense / Public Domain. However some 3rd party libraries are used too:
- Scanline rasterizer is based on Chris Hecker's "Perspective Texture Mapping" series (1995-1996),
specifically
SUBAFXFL.CPP. - Modified version of Cameron Hart's radixsort, zlib license.
- Mesh adjacency calculation based on Arseny Kapoulkine's meshoptimizer, MIT license.
- Parts were based on
mini3dfrom Playdate SDK examples (Created by Dave Hayden on 10/20/15, Copyright © 2015 Panic, Inc.) and on mini3d-plus (MIT license), but by now I have replaced them. - (only on PC): stb_image.h: Public Domain / MIT.
- (only on PC): Sokol: sokol_app, sokol_audio, sokol_gfx, sokol_time. zlib/libpng license.




