The Tilt of Tiltford: robot jousting

Two real robot arms with 3D-printed swords fence each other on stepper rails while a crowd plays from their phones. Built at HackCMU 2026.

A stepper driver board on a plywood base, its loose leads tagged X LIMIT and Y LIMIT
Fri 11:03 PM
A teammate leaning across two tables, wiring up the aluminium extrusion rails
Sat 2:44 AM
The Bambu Handy app showing the Duck-A1 printer at 49 percent, about 52 minutes left
Sat 5:53 AM
A lit campus path at dawn beside a brick building, under a pale cloudy sky
Sat 6:50 AM
Looking up past the arm's gripper at the slatted ceiling
A hand holding a twelve-LED NeoPixel ring wired to a small board
Two teammates working late in the lab, one at a laptop and one sitting on the floor
Both arms set out on the tables with a tape measure run between them
The arm holding a black printed blade, raised over the rail
The Tilt of Tiltford — trailer. Also on YouTube.

What it is

Two real SO-100/SO-101 robot arms, each holding a 3D-printed sword, fence each other on stepper rails at a medieval festival. Players fight from their phones. A crowd watches. It is an HRI experiment in whether a machine can make a room of people cheer.

We built it at HackCMU 2026, on 12 September 2026.

Two robot arms facing each other on rails, swords raised en garde
En garde. Both arms on their rails, blades up, before the first beat.
The Tilt of Tiltford title screen, a medieval festival scene
The big screen the crowd watches.

Two phones, one arena

Two players scan QR codes, name their knight, tune a deck, and plan three moves in secret. The big screen holds the rules, so nobody can cheat: phones send intents to a long-polled mailbox server and the host resolves them.

The host is resumable, seats can be reissued, and the plan timer auto-locks a player who has wandered off — all the things that go wrong when the arena is a festival and the players are strangers.

There is also a single-player campaign against three AI knights: Bluebell the squire, Sir Percival, and the Iron Champion.

Lobby screen showing two QR codes, one for the red knight and one for the blue knight
The lobby: two codes, two knights.
The phone controller showing a hand of cards
What a player holds: a hand of cards and three slots to fill in secret.

The game

Attacks land on a high line or a low line. Guards cover a line; feints sell a line you are not using. Voltage loads with feints and pays out later.

Rush charges down the rail and beats any swing — but a guard stops it cold. Two rushes in a row make a Full Tilt, which goes through any guard. Counter throws a blow back at the knight who sent it, and is spent once used. Hands persist at five cards, so what you spent last round is what you are missing this round.

Marla the tavern keeper coaches from the side of the screen, and a herald calls the tilt. The festival score is synthesised live — no samples — and the arms have a knight foley of their own.

The planning phase, showing three move slots and a voltage meter
Planning: three moves, chosen in secret, with voltage loaded from feints.
Hovering the Counter card shows a preview of what it would do
Counter throws a blow back at whoever sent it, and is spent once used.
The exchange resolving on screen with impact effects
The exchange resolves.
The two knights in their versus poses before a bout
Versus.

The robots

Every move was designed in MuJoCo, then tuned on the real arms, and the moves are chained together by a transition library so one motion can run into the next. The game waits on the real motions — the screen does not resolve an exchange until the arms have actually fought it.

The real arms

The arms fenced for real. Three exchanges were filmed, and they are in the trailer: an overhead chop stopped by the high bar, an overhead chop landing against a low guard, and a low slash from the left stopped by the right-hand guard.

Two real robot arms mid-exchange: an overhead chop caught on the high bar
An overhead chop, stopped by the high bar.
Two real robot arms mid-exchange: an overhead chop coming down against a low guard
An overhead chop, landing against a low guard.
Two real robot arms: a low slash from the left stopped by the right-hand guard
A low slash from the left, stopped by the right-hand guard.
The same low slash and right-hand guard, rendered in MuJoCo simulation
The same exchange designed in MuJoCo, blades meeting 3 mm from the guard.

Calibration, by hand

The arms’ servo calibrations live in the repo, in calib/. We calibrated the SO-100 by holding it at the zero pose and sweeping every joint to its stops, then checked the zero points against physical references — the arm leaning fully back on its board, the upright square pose, the blade sitting on top of the gripper — before we trusted the taught moves on the real arm.

Behind the scenes: a hand holding the arm at a reference pose during calibration
Calibration, by hand.

Driving the arms live

At the start of a match the game prepares the hardware: home the rails, move apart, both arms to rest. It drives the carriages together for the charge and apart for the return, plays each beat on both arms, and waits on the arms’ own busy flags rather than on timers. The screen beat is stretched to the real motion — an overhead chop is 4.5 s of arm time, not the 1.4 s the screen would take on its own.

A mock daemon carrying the real motion timings lets the whole thing be rehearsed with no hardware attached. In rehearsal the screen never released before the arms, with a worst margin of +23 ms.

Players choose their own openers and finishing flourishes. The En garde opener runs before the first beat; the Samurai finish runs after the knockout, with the winner raining blows while the loser collapses. Both stream the rail carriages in time with the arm motions.

The arms sit on stepper rails and charge in to 19.5 inches of each other.

En garde opener.
Samurai finish — the winner rains blows while the loser collapses.
Two swords meeting at the moment of impact in simulation
The moment two blades meet.
The move pipeline from MuJoCo simulation through to the real arms
From MuJoCo to the arms.

The hardware

We printed a fencing gripper for the arms: a sword holder that takes swappable 6 and 8 inch blades in five styles — fang, katana, rapier, falchion and crystal — plus a shield, a gauntlet and a sword-breaker talon for the wrist.

For the tilt itself we printed a plate with one of each 8 inch blade, plus the gauntlet and talon wrists.

CAD render of a full arm holding an 8 inch fang blade
A full arm with the 8 inch fang blade.
Turntable render of the five blade styles: fang, katana, rapier, falchion, crystal
Five blades: fang, katana, rapier, falchion, crystal — at 6 and 8 inches.
CAD render of the shield, gauntlet and sword-breaker talon wrists
Shield, gauntlet, and the sword-breaker talon.

The Arm Studio

To make all of that, we built our own animation platform. The Arm Studio holds the move library with its MuJoCo clips; the Theatre, where foley is locked to a clip; the interactions matrix of every card against every card; and the Playhouse, where two arms play a scene together.

The Arm Studio interface with the move library and clip timeline
The Arm Studio: move library, Theatre, interactions matrix, Playhouse.

Team: Chris Shi, Avnith Vijayram, Siddharth Radhakrishnan

Code: github.com/avnithv/robot-jousting