December 24, 2025•blog
I sat down in late 2025 and decided I was going to get up to speed on the state-of-the-art in AI-driven software development. Despite my move into medical technology and business, AI is too exciting a world to leave behind. This chronicles my first experiment where I used AI to perform the bulk of the development but still kept the reins in my hands.
The problem I set out to solve is that of 2d inverse kinematics. Given a robot and a desired end position, we have to find its joint angles to reach that position. We’d also like to be able to respect our joint limits and avoid colliding with the world. Here’s an example of that:
The blue/green dot represents the desired end effector position. When the dot is blue, the end is locked in the horizontal position. When the dot is green, the end effector is allowed to reach the target point from any angle.
Full source code is on GitHub.
The final project implements five solvers for comparison. Each solver can solve with:
The solvers are
| Solver | Algorithm | Notes |
|---|---|---|
jax |
gradient descent | Solver is @jax.jit-ed |
torch |
gradient descent | With dynamic graph |
sympy |
gradient descent | sympy produces equations, scipy minimizer solves them |
symbolic |
gradient descent | sympy produces and solves equations |
fabrik |
FABRIK | Very cool algorithm! |
symbolic and sympy were not able to run with collision checking, so were omitted from these. I produces the same trajectory from jax, torch, and fabrik and was able to obtain these solution times, in log milliseconds:
jax is incredibly fast, even on the CPU! I’ve been impressed with its speed in the past, but it still surprised me with its speed. Torch (without precompilation) is much slower, likely because of the overhead per operation. FABRIK runs very fast, but jax outstrips even that. The overhead from first compiling the function is only about 1000 ms and only has to be paid once.
FABRIK, being relatively new, has no widely-accepted standard for handling collisions. I got Claude to implement a relatively naive solution to no-go zones involving projection onto the boundary, and it sort-of works. FABRIK gracefully handles joint limits and end-effector angles, but remains a little vulnerable to degenerate/pathological cases. Here’s an example from our FABRIK implementation:
When it comes time to use this in a non-safety-critical robot, I’d be comfortable with deploying the jax solver with some added guardrails and sanity checks. This is what JAX looks like:
All this was implemented with extensive use of Anthropic’s Claude Sonnet 4.5, which is incredible! It required a little strategic prompting, but generally acted like a mid-level developer with a caffeine addiction. It came up with the data model and forward kinematics easily, and was able to debug issues relatively reliably.
The general approach I followed in writing the code was to plan this myself and have Claude implement them. Instead of giving Claude an overall plan, I had it implement features or parts of roughly the size I would do myself, followed by testing and integration. Basically, I used Claude interactively in the way one might use a debugger or a fuzzer.
Claude occasionally stumbled but was easily prompted into correcting its mistakes. The most notable thing about the stumbles is that I would have easily made all these myself on the road to completing this project. Examples include:
jax, etc.The problem itself is only moderately difficult, and a lot of the key components are taught at the undergrad level. Claude was able to complete these tasks, and also harder ones that required significant generalization from its training corpus or abilities I didn’t think it had.
The next step on this journey is to build a much bigger project with an AI-first approach, starting from the design document.