Jet is a dependency free software rasteriser for ESP32 and other small devices
A tiny, dependency-free, fixed-function 3D rasteriser written in modern C++17 for embedded devices (ESP32, etc)
At a glance
- What is it?
- Jet is a fixed-function 3D software rasteriser in C++17 with no dependencies, using integer arithmetic in the rasterisation path and native RGB565 output for embedded displays, with compile-time switches that trade features for CPU, RAM and flash. It suits an engineer building a 3D scene on an ESP32 or STM32, and it is a renderer, not an engine.
- Who is it for?
- Adopt Jet if you need to draw 3D geometry on hardware with no GPU and no appetite for a dependency tree, because Jet is a self-contained rasteriser with integer arithmetic in the hot path, native RGB565 output and compile-time switches that let you remove features to fit flash and RAM on an ESP32 or STM32.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 28 days ago.
- What is it written in?
- Mainly C++, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A rasteriser, not an engine
Jet is described as a dependency-free, fixed-function 3D software rasteriser written in C++17, and that description sets the scope precisely. It targets embedded hardware with limited CPU and memory, including ESP32 and STM32, and also runs on desktop PCs and Raspberry Pi. The dependency-free part is the striking claim: no graphics library, no math library, no asset pipeline toolchain, just the code. The word fixed-function describes the pipeline, since shading modes are selected per surface from a fixed set, with flat, Gouraud, Phong and wireframe shading available, and texture mapping that is affine or perspective-correct with optional bilinear filtering. The last part of the design brief is about how it moves pixels. Jet uses integer arithmetic in the rasterisation hot path and native 16-bit RGB565 colour for output to embedded displays, which is what makes a software rasteriser viable on a microcontroller at all. The library is MIT licensed, the repository is not archived, and the last push was on 2026-09-25. There are no GitHub releases, so the build system and the branch history are the record of the project.
What the throughput figure actually measures
The README makes a performance claim and, unusually, scopes it in the same sentence. On an ESP32-S3, Jet achieves up to 100k tris/sec in the tropical island example, and the claim points at a validation document in the JetExamples repository. The scope matters more than the number. This measures rasterized triangles over render time, excluding display scanout and frame pacing, and the README states that throughput varies with scene complexity, triangle size and enabled rendering features. So the figure is a rasterisation rate for one particular scene, not a frame rate. The distinction is the one that matters when you are budgeting an embedded project: on a microcontroller, the time to shift a frame out over SPI to the panel, and the time you spend pacing frames, are separate costs that the number does not include. The README also points to an ESP32 optimisation document for exact indexed-texture results, memory costs and supported sampling paths, which is where the numbers you can plan against actually live.
Compile-time switches as a memory strategy
The feature list is long, and the interesting engineering is not in the features themselves but in the mechanism for having them. Compile-time switches in JetConfig.hpp let you disable features to reduce CPU, RAM and flash usage, with the available options shown in src/JetConfig.example.hpp. On an ESP32-S3 with a few hundred kilobytes of RAM, that is the whole design philosophy in one sentence: nothing is on unless you turn it on. The features you can remove or keep include shared mesh instancing, Z-buffering, the reciprocal-Z depth option, screen-space picking, tiled textures and the megatexture path. The README is honest that some of these carry a cost. Opt-in shared mesh instancing reduces repeated geometry storage with independent placements and shading overrides. Screen-space picking is described as compile-time bounded and zero cost when set to 0. The optional JET_PERSPECTIVE_DEPTH reciprocal-Z interpolation adds a pixel division and defaults off. And the preciseDepthSort refinement of the painter's algorithm sorts occupied buckets by triangle mean depth for overlapping parts without a depth allocation, but it is opt-in and does not resolve intersecting or cyclic polygons. That last caveat is the sort of thing that costs a weekend if you assume otherwise.
Depth handling and the painter's algorithm path
Depth is where embedded renderers usually compromise, and Jet offers both routes. You can use an optional Z-buffer, or painter's-algorithm sorting, either per-object or per-triangle. The painter path avoids a depth allocation entirely, which matters when memory is the constraint, and the preciseDepthSort refinement exists to make it behave better when objects overlap. The Z-buffer path is the more correct one, and the README documents the desktop depth accuracy option JET_PERSPECTIVE_DEPTH for large sloping faces, used with Z_BUFFERING=1 and FAST_Z=0, at the price of a pixel division. Elsewhere there is backface and frontface culling, depth bias for decals and shadows, and per-object blend modes covering replace, add, subtract, multiply, average and XOR. Transparency itself is handled without a separate pass, using screen-door alpha and noise-based dithering, which is a legitimate embedded technique and also an approximation you should know about before you use it for a UI.
Display-side tricks: half width buffers and fields
A set of features exists purely because the target is a small panel. Half-width framebuffers render at half the horizontal resolution and are upscaled, halving the memory cost of the colour buffer. Interlaced field buffers render in alternating fields with optional reconstruction, and checkerboard rendering is another variant of the same idea. On HALF_WIDTH_BUFFERS builds, 2D sprites are composited at full output resolution during display scanout, so the overlay stays sharp even though the 3D scene underneath is rendered narrow. The Sprite2D class draws overlays after render and supports textured or solid-colour fills, colour-key transparency, alpha and additive blending, integer upscaling and zOrder-based draw order. There is also a display integration path where the caller passes a line, a width and a y coordinate to the renderer's compositeSprites function. The SSR_FIELD_REFLECT mode exists for a related reason: when field buffers are active, the water reflection mode samples mirror pixels from the previous committed field buffer so that reflections do not depend on draw order across parallel rendering bands.
The example projects are the real documentation
The README points to a separate repository, JetExamples, containing sixteen standalone ESP-IDF projects, from a reusable rotating-cube template and individual feature demos to two complete demoscene scenes, with build instructions, reference wiring, and display and SPI configuration for ESP32-S3 and P4. That last list is the practical content for anyone starting: the wiring and the SPI configuration are the parts you cannot derive from the library source. Those scene projects also describe the rendering budget honestly, since the native captures use 480x320 output, RGB565 colour and half-width interlaced field buffers with the performance overlay hidden. The scenes include a Utah teapot with glossy Phong lighting, a tropical island with rippled water, reflections and lens flares, an ESP 88 neon city cinematic with rain and a car chase, and MATTER, three minutes of procedural sculpture. The Neon Motorworks project combines runtime OBJ loading, textured Phong paint and environment-mapped windows. There is also a separate experimental repository for ESP32-S3 megatextures that adapts a N64 chapel using paletted tiled textures and an application-managed cache of bilinear samples, with the API contract and limitations in docs/TiledTextures.md. The repository itself carries a CMakeLists.txt, a component.mk, an idf_component.yml, a library.json and a library.properties, so it is consumable both through PlatformIO and the ESP-IDF component manager, and the tests/ and examples/ directories are visible in the tree.
Where Jet is the wrong choice
Four boundaries are worth stating. The first is scope. Jet renders triangles; it does not load scenes, manage animation, handle input, run a physics step or provide an asset pipeline. The minimal Wavefront .obj loader and the Primitives::create* helpers for cubes, spheres, cylinders, capsules, pyramids, grids, planes, quads and billboards are conveniences, not a framework. If you are building a game, you are writing the game and Jet is the renderer underneath it. The second is that a software rasteriser is a fallback, not a goal. On a device with any kind of GPU or acceleration, a hardware path wins, and on a desktop PC the fixed-function software pipeline is a learning exercise rather than a production choice. The third is the maturity and community signal. The README says to expect rough edges only about the MCP-style disclaimers elsewhere; what it does say is that issues and pull requests are welcome, and the practical facts are that there are no GitHub releases, the last push was on 2026-09-25, and the tracked performance numbers live in a separate examples repository, so the release discipline is informal. The fourth is that the fixed-function pipeline is by definition not extensible in the usual sense, and while there is a custom shader entry point for extending it, you are adding to a pipeline designed to be complete rather than bending one designed to be extended. The realistic alternative for the same goal is a scene graph with an embedded renderer, which handles asset loading, transforms and shading for you and, on a host system, can use hardware acceleration instead, and the alternative for pure embedded use is to compute fewer pixels, which is a budget decision rather than a library one.
Editorial conclusion
Adopt Jet if you need to draw 3D geometry on hardware with no GPU and no appetite for a dependency tree, because Jet is a self-contained rasteriser with integer arithmetic in the hot path, native RGB565 output and compile-time switches that let you remove features to fit flash and RAM on an ESP32 or STM32. Do not adopt it if you need a scene graph, an asset pipeline, animation or input handling, since those live in the example projects and in your own code, and do not adopt it if a GPU is available, because a hardware accelerated path will beat a software rasteriser. Verify two things before you commit: that the display and SPI configuration in the JetExamples repository matches your panel, since the examples carry reference wiring for ESP32-S3 and P4, and that the rendering features you need are not behind a switch you plan to disable for memory reasons. The practical first step is to build one of the sixteen standalone ESP-IDF examples from JetExamples, get a cube rotating on your display, and only then start trading features for RAM.
Frequently asked questions
What is the Jet 3D rasteriser?
It is a dependency-free, fixed-function 3D software rasteriser written in C++17 that targets embedded hardware such as ESP32 and STM32 and also runs on desktop PCs and Raspberry Pi. It uses integer arithmetic in the rasterisation hot path and native RGB565 colour for embedded displays.
How fast is Jet on an ESP32-S3?
The README states that on an ESP32-S3 Jet achieves up to 100k tris/sec in the tropical island example, measured as rasterized triangles over render time and excluding display scanout and frame pacing. The README also notes that throughput varies with scene complexity, triangle size and enabled rendering features.
How do I reduce the memory Jet uses on a microcontroller?
Compile-time switches in JetConfig.hpp disable features to reduce CPU, RAM and flash usage, and the available options are listed in src/JetConfig.example.hpp. Half-width framebuffers and interlaced field buffers reduce colour buffer memory, and screen-space picking costs nothing when its compile-time bound is set to 0.
Does Jet need a Z-buffer?
No. You can use an optional Z-buffer, or painter's-algorithm sorting per object or per triangle. The Object::preciseDepthSort refinement sorts occupied painter buckets by triangle mean depth for overlapping parts without a depth allocation, but it is opt-in and does not resolve intersecting or cyclic polygons.
Where are the example projects for Jet?
In the separate JetExamples repository, which holds sixteen standalone ESP-IDF projects from a rotating-cube template to feature demos and two demoscene scenes, with build instructions, reference wiring and display and SPI configuration for ESP32-S3 and P4. There is also a separate repository for the experimental ESP32-S3 megatexture demo.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/cubecoders-jet)Was this useful?