Inspiration

Our first idea was to have four people use their phones to control one character, with each person controlling a different limb. We refined that into two players sharing one fighter: one controls the legs, while the other controls the arms. That gave us the heart of Limbpossible—a fighting game where winning takes coordination with the person standing next to you.

The Mystery Track brought everything together: phone motion, friendly competition, and a game for four or more people.

What it does

Limbpossible turns your phones into motion controllers for a multiplayer arena brawler. Players scan a QR code to join a shared lobby, get paired with a teammate, choose a fighter, and vote for an arena.

Tilt your phone to move or turn, and give it a quick jolt to jump or punch. Your goal is to knock rival fighters off the platform while keeping your own shared body under control. Every match mixes teamwork, rivalry, and plenty of opportunities to blame anyone but yourself.

How we built it

We built the arena in Three.js, with Node.js, Express, and Socket.IO connecting the host screen to each player’s phone browser.

The phones use browser orientation readings to measure tilt and accelerometer readings to detect deliberate jolts. We calibrate a comfortable holding position, smooth the tilt input, and translate those movements into walking, turning, jumping, and punching.

The host runs the game simulation and handles movement, collisions, damage, and ring-outs. The room server manages joining, player assignments, voting, and reconnects.

Challenges we ran into

We initially wanted to build a mobile app, but distributing a development build would make joining a game difficult. We wanted friends to jump in quickly, without installing an app or enabling developer mode on their phones. That led us to a browser-based approach: scan a QR code, allow motion access, and join the room.

Making phone movement feel predictable was another challenge. Sensor readings can be noisy, and everyone holds their phone differently. We used calibration, smoothing, dead zones, and action cooldowns to turn those readings into reliable controls.

We also had to keep multiple phones connected to one shared game state, handling disconnects, motion permissions, and stale inputs so the match could recover when a controller lost its connection.

Accomplishments that we're proud of

We’re proud of our live announcer, which connects Gemini and ElevenLabs to the action in the arena. The game detects events such as heavy hits, close calls, hazards, and match results, then sends Gemini context about the fighters and what just happened. Gemini writes a short reaction, and ElevenLabs turns it into speech while matching captions appear on the host screen.

Making the announcer feel timely was a challenge of its own. We had to keep commentary brief and avoid letting old reactions pile up while the fight moved on. Hearing the announcer react to a moment we had just created made the game feel like a live sporting event.

What we learned

We learned how challenging it is to access reliable phone motion data through a browser. Sensor permissions, HTTPS requirements, and differences between devices and browsers all affect whether tilt and accelerometer readings are available and usable. Getting those readings was only the first step—we also had to calibrate and smooth them into controls that felt predictable.

We chose the browser because we wanted everyone to join easily. Scanning a QR code removes the need to install a development build or enable developer mode. Working through those limitations taught us how much engineering goes into making an experience simple for the people using it.

What's next for Two Body Brawl

We want to playtest with more groups and phones, refine motion sensitivity, and make onboarding even faster. We’d also like to add more arenas, team challenges, and ways to switch partners between rounds.

The next goal is to make Limbpossible something anyone can bring to a game night: open a room, scan in, and start arguing about whose fault that last ring-out was.

Share this project:

Updates

Submission history