Differentiable implicit CAD in the browser: edit via GUI or code, solve constraints, render with WebGPU raymarching, optimize shapes end-to-end.
46
stars
336
commits
Python
primary language
Sep 8, 2026
updated
Differentiable code-first CAD: sketches, constraints, SDF geometry, meshing and FEM simulation composed into one function JAX can differentiate end to end.
[!WARNING] The API is not stable. Expect breaking changes.
The handle under the pointer is a named Vector2 in scene.py, and the
joint it moves is a smooth union of three extrusions. Dragging it writes the
parameter buffer the shader already reads, so the blend follows at frame
rate; releasing it patches the literal in the source. The source is the
model.
One Python program declares the whole thing, and every arrow is a derivative JAX can take:
sketch vertices ─► constraints ─► SDF ─► mesh ─► FEM solve ─► objective
└─────────────────────────── ∂J/∂θ ◄───────────────────────────┘
A sketch profile is a list of named Vector2 parameters. Constraints are
residuals on those parameters, solved by Newton projection onto the constraint
manifold. Extruding or revolving the profile produces an SDF that shares those
parameter objects. Dual contouring turns the field into a surface, TetGen or
Gmsh fills it, and jax-fem or CalculiX solves on it. A study is declared in the
scene beside the geometry it loads — here the end cap's ThermalStudy, a heat
flux on the bearing boss and a fixed temperature on the flange — and solving it
is one click, meshing included:
The same study is a function of the sketch, so jax.grad reaches from its
objective back to a fin's tip coordinate. An Optimization declared in the
scene descends on that gradient, every step a mesh, a solve and an adjoint,
projected back onto the constraints. The heat sink below starts deliberately
overbuilt and slims as its peak temperature falls:
A work plane taken from a face (SketchPlane.on(body.cap("+"))) is an
expression over the parent feature's parameters, so a boss extruded from it
differentiates with respect to its parent's depth. Every sketch's plane is
drawn in the viewport with its origin and normal; the sketch tool shows where
a new one will land before it exists, and a plane written as literals can be
taken by its frame and moved, which rewrites the SketchPlane(origin=...)
in the source:
No boundary representation is stored: the field is the model, and every surface is derived from it at a resolution you choose.
apply/vjp interface, run in-process, in a container or remotelygit clone https://github.com/andrinr/cadjoint
cd cadjoint
uv sync # CPU JAX — macOS, Linux, Windows
uv sync --extra cuda # Linux + NVIDIA GPU instead
Everything beyond the geometry core is an extra, one --extra per name:
| Extra | Pulls in |
|---|---|
fem | jax-fem finite-element stack (basix, meshio, petsc4py, gmsh) |
tesseract | tesseract-core + tesseract-jax, the plugin runtime |
gmsh | Gmsh in-process for the tet10 mesher (GPL, hence its own extra) |
viewer | Jupyter widget (anywidget) and the playground's process monitor (psutil) |
editor | playground lint, completion and signature help (ruff, jedi) |
stepcheck | OCCT validation of STEP exports (dev) |
docs | Quarto API reference (quartodoc) |
Avoid --all-extras on macOS: cuda has no macOS wheels.
uv run cadjoint-viewer --open # serves http://127.0.0.1:8765/
The editor on the left holds scene.py; the viewport shows the compiled
field. Vertex handles, gizmos, material swatches, face picks, constraint chips
and solver runs all write back into the source. A properties window follows
the selection and shows every argument the call was written with: literals
as fields you can edit, expressions as the text they are.
Three desks, Model, Sketch and Simulate, share one viewport. Studies solve with one click, results land in a labelled legend, and a declared optimization streams step by step and writes its optimized values into the program.
The server only listens on localhost and compiles each edit in a timed child process, but it executes Python on your machine: only run code you trust.
The playground guide walks through every window with recorded clips.
from cadjoint.construction import PolygonProfile, SketchPlane, extrude
from cadjoint.constraints import DistanceConstraint, satisfy_constraints
from cadjoint.geometry import Scalar, Vector2
from cadjoint.meshing import GridSpec, extract_mesh
a = Vector2([0.0, 0.0], free=True, name="a")
b = Vector2([1.0, 0.0], free=True, name="b")
c = Vector2([1.0, 1.0], free=True, name="c")
d = Vector2([0.0, 1.0], free=True, name="d")
DistanceConstraint(a, b, 1.2)
depth = Scalar(0.5, free=True, name="depth")
block = extrude(PolygonProfile([a, b, c, d], plane=SketchPlane()), depth=depth)
satisfy_constraints(block) # projects a and b onto the constraint, in place
grid = GridSpec.from_bounds((-0.5, -0.5, -0.5), (2.0, 2.0, 1.5), 32)
mesh = extract_mesh(block, grid)
functionalize(block) returns a pure function of the free parameters, so
jax.grad of anything downstream reaches a, b, c, d and depth. The
getting-started guide
continues from here to meshing, a thermal study and an optimization;
examples/fem_bracket_optimization.py runs the full chain from the command
line, with the adjoint checked against finite differences at every boundary.
uv run pytest tests -q --ignore=tests/fem # fast gate
uv run pytest tests/fem -q # needs the fem extra; CalculiX via CADJOINT_CCX
uv run pre-commit install # ruff on commit
The playground UI is a Solid + TypeScript app in frontend/, built into
cadjoint/viewer/static and committed, so installing cadjoint needs no Node
toolchain. npm run dev proxies to a running server, npm run build refreshes
the bundle, npm test and npm run e2e run the unit and Playwright suites.
Docs need Quarto and the docs extra:
uv run quartodoc build then quarto preview.
Performance work is measured, not guessed: benchmarks/jax_compile_profile.py
breaks a worker request into trace, lower, XLA compile and cache time per
program, and research/performance.md records what
each lever bought.
cadjoint was an entry in the
Tesseract Hackathon 2026 (Track 01,
inverse design and shape optimization). That state is frozen on the
tesseract-hackathon-2026
branch. Inspired by Fidget and
Inigo Quilez's distance functions.
336 commits
Python
68.6%
TypeScript
26.8%
CSS
2.5%
JavaScript
1.6%
Differentiable implicit CAD in the browser: edit via GUI or code, solve constraints, render with WebGPU raymarching, optimize shapes end-to-end.
46
stars
336
commits
Python
primary language
Sep 8, 2026
updated
Differentiable code-first CAD: sketches, constraints, SDF geometry, meshing and FEM simulation composed into one function JAX can differentiate end to end.
[!WARNING] The API is not stable. Expect breaking changes.
The handle under the pointer is a named Vector2 in scene.py, and the
joint it moves is a smooth union of three extrusions. Dragging it writes the
parameter buffer the shader already reads, so the blend follows at frame
rate; releasing it patches the literal in the source. The source is the
model.
One Python program declares the whole thing, and every arrow is a derivative JAX can take:
sketch vertices ─► constraints ─► SDF ─► mesh ─► FEM solve ─► objective
└─────────────────────────── ∂J/∂θ ◄───────────────────────────┘
A sketch profile is a list of named Vector2 parameters. Constraints are
residuals on those parameters, solved by Newton projection onto the constraint
manifold. Extruding or revolving the profile produces an SDF that shares those
parameter objects. Dual contouring turns the field into a surface, TetGen or
Gmsh fills it, and jax-fem or CalculiX solves on it. A study is declared in the
scene beside the geometry it loads — here the end cap's ThermalStudy, a heat
flux on the bearing boss and a fixed temperature on the flange — and solving it
is one click, meshing included:
The same study is a function of the sketch, so jax.grad reaches from its
objective back to a fin's tip coordinate. An Optimization declared in the
scene descends on that gradient, every step a mesh, a solve and an adjoint,
projected back onto the constraints. The heat sink below starts deliberately
overbuilt and slims as its peak temperature falls:
A work plane taken from a face (SketchPlane.on(body.cap("+"))) is an
expression over the parent feature's parameters, so a boss extruded from it
differentiates with respect to its parent's depth. Every sketch's plane is
drawn in the viewport with its origin and normal; the sketch tool shows where
a new one will land before it exists, and a plane written as literals can be
taken by its frame and moved, which rewrites the SketchPlane(origin=...)
in the source:
No boundary representation is stored: the field is the model, and every surface is derived from it at a resolution you choose.
apply/vjp interface, run in-process, in a container or remotelygit clone https://github.com/andrinr/cadjoint
cd cadjoint
uv sync # CPU JAX — macOS, Linux, Windows
uv sync --extra cuda # Linux + NVIDIA GPU instead
Everything beyond the geometry core is an extra, one --extra per name:
| Extra | Pulls in |
|---|---|
fem | jax-fem finite-element stack (basix, meshio, petsc4py, gmsh) |
tesseract | tesseract-core + tesseract-jax, the plugin runtime |
gmsh | Gmsh in-process for the tet10 mesher (GPL, hence its own extra) |
viewer | Jupyter widget (anywidget) and the playground's process monitor (psutil) |
editor | playground lint, completion and signature help (ruff, jedi) |
stepcheck | OCCT validation of STEP exports (dev) |
docs | Quarto API reference (quartodoc) |
Avoid --all-extras on macOS: cuda has no macOS wheels.
uv run cadjoint-viewer --open # serves http://127.0.0.1:8765/
The editor on the left holds scene.py; the viewport shows the compiled
field. Vertex handles, gizmos, material swatches, face picks, constraint chips
and solver runs all write back into the source. A properties window follows
the selection and shows every argument the call was written with: literals
as fields you can edit, expressions as the text they are.
Three desks, Model, Sketch and Simulate, share one viewport. Studies solve with one click, results land in a labelled legend, and a declared optimization streams step by step and writes its optimized values into the program.
The server only listens on localhost and compiles each edit in a timed child process, but it executes Python on your machine: only run code you trust.
The playground guide walks through every window with recorded clips.
from cadjoint.construction import PolygonProfile, SketchPlane, extrude
from cadjoint.constraints import DistanceConstraint, satisfy_constraints
from cadjoint.geometry import Scalar, Vector2
from cadjoint.meshing import GridSpec, extract_mesh
a = Vector2([0.0, 0.0], free=True, name="a")
b = Vector2([1.0, 0.0], free=True, name="b")
c = Vector2([1.0, 1.0], free=True, name="c")
d = Vector2([0.0, 1.0], free=True, name="d")
DistanceConstraint(a, b, 1.2)
depth = Scalar(0.5, free=True, name="depth")
block = extrude(PolygonProfile([a, b, c, d], plane=SketchPlane()), depth=depth)
satisfy_constraints(block) # projects a and b onto the constraint, in place
grid = GridSpec.from_bounds((-0.5, -0.5, -0.5), (2.0, 2.0, 1.5), 32)
mesh = extract_mesh(block, grid)
functionalize(block) returns a pure function of the free parameters, so
jax.grad of anything downstream reaches a, b, c, d and depth. The
getting-started guide
continues from here to meshing, a thermal study and an optimization;
examples/fem_bracket_optimization.py runs the full chain from the command
line, with the adjoint checked against finite differences at every boundary.
uv run pytest tests -q --ignore=tests/fem # fast gate
uv run pytest tests/fem -q # needs the fem extra; CalculiX via CADJOINT_CCX
uv run pre-commit install # ruff on commit
The playground UI is a Solid + TypeScript app in frontend/, built into
cadjoint/viewer/static and committed, so installing cadjoint needs no Node
toolchain. npm run dev proxies to a running server, npm run build refreshes
the bundle, npm test and npm run e2e run the unit and Playwright suites.
Docs need Quarto and the docs extra:
uv run quartodoc build then quarto preview.
Performance work is measured, not guessed: benchmarks/jax_compile_profile.py
breaks a worker request into trace, lower, XLA compile and cache time per
program, and research/performance.md records what
each lever bought.
cadjoint was an entry in the
Tesseract Hackathon 2026 (Track 01,
inverse design and shape optimization). That state is frozen on the
tesseract-hackathon-2026
branch. Inspired by Fidget and
Inigo Quilez's distance functions.
336 commits
Python
68.6%
TypeScript
26.8%
CSS
2.5%
JavaScript
1.6%