Threepp
A cross-platform C++20 3D library with the high-level API of three.js
Install / Use
npx skills add markaren/threeppInstalls into whichever agent you are using.
README
threepp
A cross-platform C++20 3D library with the high-level API of three.js — and modern backends: portable OpenGL, and a deferred Vulkan renderer with ray-traced accents.
On top of that: a scene editor, PhysX-backed robot simulation, and ground-truth sensor output for synthetic-data generation.
Real-time FFT ocean — Vulkan deferred-hybrid renderer (raster-first, with ray-traced shadows & reflections) (examples/vulkan/vulkan_ocean.cpp)
The scene editor — hierarchy, inspector, Play mode, and a live sensor preview (apps/editor, doc/editor.md)
You get a scene graph, materials, lighting, cameras, render loop, controls, loaders, all composable, in a handful of lines.
📖 New here? Start with Getting started — a guide to the concepts the whole library is built from: the scene graph, ownership and lifetimes, geometry/material, lights, the frame loop, loaders and the two backends.
Highlights
Rendering
- Two backends behind one scene graph: OpenGL 3.3 raster (the portable baseline, also the Emscripten/WebGL2 target) and a deferred Vulkan renderer (raster G-buffer with ray-traced AO, GI, reflections and shadows; denoised, with TAA).
Ocean— three-cascade FFT-displaced water with foam, transmission and live wind. Vulkan only; on OpenGL the type is an inert flat plane.- PMREM environment maps.
- Gaussian splatting —
SplatCloudrenders scans on both backends, with two loaders (INRIA.ply, PlayCanvas SOG) and dynamic LOD on Vulkan.
Simulation & perception
- Ground-truth labels straight out of the render (Vulkan) — one call reads the
G-buffer in its native format: float32 metric depth, per-object instance ids that
survive add/remove/hide/LOD, semantic classes, world normals and motion.
addViewattaches N cameras rendered from one scene build in a single submission, so a whole sensor rig sees the same simulated instant. - Simulated sensors — LIDAR (VLP-16/HDL-32E/OS1-64/OS0-128 patterns), depth and event cameras, plus IMU, joint encoder, contact and 6-axis force/torque. Ray-traced on Vulkan, rasterized on OpenGL, with the same range semantics on both. Every measurement is seeded and sim-clock-stamped, so a recorded run replays bit-for-bit.
- Robots, not just rigid bodies — PhysX reduced-coordinate articulations built from a URDF or xacro in one call, with joints, limits, PD drives, V-HACD concave colliders, and a header-only damped-least-squares IK solver. From C++ and Python.
- Physical camera, lens and sensor model — EV100 exposure from an aperture/shutter/ISO triplet, photometric light units, OpenCV-convention intrinsics, Brown-Conrady and fisheye distortion applied to both the image and the labels, and electron-domain sensor noise. All off by default; it expects a scene lit in real photometric units.
Tooling
- A scene editor and a headless player — author scenes with physics, sensors and Python behaviour scripts, then replay them in CI. Documents are plain three.js JSON, so an authored scene runs in an ordinary threepp program with no editor present.
- Python bindings — the scene graph, headless render-to-NumPy, PhysX, and
threepp.rl(a GPU-vectorized RL stack). - Built-in loaders — models [STL (binary & ASCII), OBJ/MTL, glTF/GLB incl. meshopt compression,
COLLADA, SVG, URDF/xacro], images [PNG/JPEG, DDS, WebP, Radiance HDR, OpenEXR] and
Gaussian-splat scans.
USDLoaderandFBXLoaderare opt-in. - Native xacro support — URDF loading takes
.urdf.xacrodirectly (macros, properties, YAML-driven parameters,$(find)/$(arg)), no ROS or Python install required; verified against the Franka and Universal Robots ROS 2 description. - Builds on Windows, Linux, macOS, MinGW and with Emscripten.
Current state of the project
Primary use case: research and education.
threeppis built for research, prototyping, and learning — not as a production-hardened engine. API and behavioural stability are not prioritized: APIs may change, and backends (especially the Vulkan backend) evolve rapidly. Pin a tag/commit if you need reproducibility, and expect to track changes if you followmaster.
There are two layers here, and they move at very different speeds.
The three.js port is mature and stable. The high-level API is mostly in line with three.js r129 with changes from newer revisions in some areas, and the OpenGL backend is a mechanical port of the WebGL renderer. It barely moves between releases, and it is what most users touch.
The simulation layer on top of it is where the project is going — the deferred Vulkan renderer, the sensor suite, PhysX robotics, the editor and the Python bindings. That is where the work goes, and it is what the stability warning above is really about.
A few limits worth knowing before you start:
- The Vulkan backend evolves fastest and breaks most often. OpenGL is the conservative choice.
- Gaussian splat clouds are a backdrop, not a simulation asset: they cast no shadows, appear in no reflection, contribute nothing to GI, are invisible to the ray-traced sensors, and are not serialized.
- The editor is tested against OpenGL; its Vulkan view pane is best-effort. See the known-limitations list in doc/editor.md.
- GPU code paths are not covered by CI — the runners have no device. Golden-image tests are a local pre-push check.
What works?
three.js parity — what the ported API gives you:
- Line, Points, Mesh, InstancedMesh
- Geometries [Box, Sphere, Plane, Cylindrical, Capsule, Tube, ++]
- Lights [Ambient, Directional, Point, Spot, Hemi, RectArea]
- Raycasting [Mesh, Line, Points]
- 2D/3D Textures, 2D/3D text, Sprites, RenderTarget, CubeMaps
- Transparency, Transmission, Shadows
- Post-processing on the OpenGL path:
EffectComposerwith render/shader/mask/ clear/texture/save passes plusUnrealBloomPassandBokehPass(depth of field), MSAA-capable internal targets, and three.js addon shaders that port over as-is (examples/postprocessing) - Environment maps, including PMREM
- Animation, morph targets, Bones
- Controls [Orbit, Fly, Drag, Transform]
- Water and Sky shaders
- Built-in text rendering and font loading [typeface.json, TTF]
- Scene serialization —
ObjectExporter/ObjectLoaderread and write three.js "Object" JSON (metadata 4.5) deterministically, with the option to reference source models and textures instead of inlining them. Documents authored by the three.js editor load as-is.
Beyond three.js — what this library adds:
- Gaussian splatting —
SplatCloudwith two scan loaders and a Vulkan compute tile rasterizer (doc/vulkan_splats.md) - Simulated sensors — LIDAR, depth and event cameras, IMU, joint encoder, contact, force/torque
- PhysX physics — rigid bodies, reduced-coordinate articulations, joints, soft bodies, vehicles, and V-HACD convex decomposition
- Automatic mesh LOD (Vulkan, on by default), GPU occlusion culling, and NVIDIA DLSS / AMD FSR 3.1 temporal upscaling
- Procedural content, all asset-free and first-party — quadtree-LOD terrain, trees, grass, conveyor systems, a parametric log cabin
- Real-world terrain — a documented "region pack" format plus an included Python tool that builds one from Norwegian national open data (Kartverket elevation, NVDB roads, OSM footprints with building heights)
- Basic Audio support using miniaudio
- Generic model loader based on Assimp (requires the assimp package — see Optional downstream dependencies)
- Easy integration with Dear ImGui
Applications
Two binaries build alongside the library (THREEPP_BUILD_EDITOR, on by default for a
top-level GLFW build):
threepp_editor— a scene editor: viewport, hierarchy, inspector, undo/redo, and a Play mode backed by PhysX. Scenes save as ordinary three.js Object JSON with everything editor-specific (physics, sensors, scripts, joints, vehicles, splines, conveyors, sound) inuserData, so a saved document opens and runs in a plain threepp program with no editor present. Python behaviour scripts attach to objects Unity-style. See doc/editor.md.threepp_player— the same play runtime with no editing machinery: headless, independent episodes, sensor CSV recording, and a nonzero exit if any script raised or the document would not play. It registers the same play sessions the editor does, in the same order, so a scene that runs under Play runs in CI. See doc/player.md.
But, but why?
Because C++ deserves nice things too.
Also, because fun.
Implementation notes
In general, you'll find that math classes are value types, while threepp expects smart pointers for other types.
For convenience, geometries, materials, etc. have a static ::create function that returns a std::shared_ptr.
Thus, you don't necessarily need to handle memory explicitly using threepp.
Furthermore, materials, geometries and textures are automatically disposed of when they go out of scope.
Yay!
threepp/threepp.hpp is a convenience umbrella over the three.js-equivalent core. Every
Related Skills
node-connect
385.5kDiagnose OpenClaw Android, iOS, or macOS node pairing, QR/setup code, route, auth, and connection failures.
blender-python-addon
40.5kBlender Python add-on rules for operators, panels, properties, registration, testing, and API-safe scripting
flutter-development-guidelines-cursorrules-prompt-file
40.5kCursor rules for Flutter development with MVVM architecture, Riverpod state management, Material widgets, and Dart style guidelines.
commit-push-pr
140.6kCommit, push, and open a PR
