Inspiration
In most games, every match is a clean slate. The NPC who lost to you five times greets you like a stranger. We wanted the opposite: AI rivals with receipts. Same opponent, different history.
What it does
Soul Link is a turn-based anime 1v1 arena where both fighters are autonomous AI agents. You pick two of six fighters and watch them go:
- Each turn, a fighter picks one of five moves (light, heavy, special, block, dodge) based on its personality, the live HP, what just happened — and its memory of past fights against this exact opponent.
- When a match ends, a summary is saved to long-term memory. Next time those two meet, the grudge is real: Rei remembers Kaito always opens heavy, so she opens with a dodge and calls him out for it.
- Every fighter trash-talks in short, Unreal-Tournament-style one-liners, voiced by a custom-designed voice per character, with an arena announcer calling FIRST BLOOD, RAMPAGE and GODLIKE.
- Matches end with a blood-red victory screen, a K.O., and the result written straight back into memory.
How we built it
- AWS Bedrock (Claude): one decision per turn. Claude must answer through a strict tool schema — exactly one of 5 legal moves plus a ≤8-word taunt — so it can't go off the rails.
- SurrealDB Agent Memory: every finished match is written as a plain-English summary; SurrealDB extracts entities and actions (who dodged what, who won). At match start we query it and keep only memories for this exact pair of fighters.
- Daytona: the whole FastAPI backend — and the hosted game itself — runs in one Daytona sandbox behind a public preview URL.
- Arize AX: every turn is one AGENT span with readable inputs (HP, memories used) and outputs (move, taunt, damage), the Claude call nested inside, and each match grouped as a session — so any weird decision can be audited.
- ElevenLabs: 7 voices built with Voice Design from character briefs (a Nordic dwarf, an ancient elven master, a smug shadow assassin…), gender-matched and pitch-checked, with fixed seeds so each character keeps the same voice. TTS is proxied through the backend so the API key never reaches the browser.
- Frontend: plain Vite + vanilla JS, pixel-arcade style: comic move callouts, floating damage numbers, hit flashes, announcer banners, and original arena-techno music synthesized live with the Web Audio API.
Safeguards
- The agent can only act through 5 legal moves; the server, not the model, resolves damage.
- 40-turn cap; timeouts and safe fallbacks if the LLM or memory is slow or down.
- PG-13 trash-talk rule; fighters may only quote stats that actually appear in their prompt — no invented history.
- Memory is filtered to the exact matchup, so fighters never act on someone else's history.
- All keys stay server-side; the TTS proxy only accepts our 7 voices and short lines.
- Every decision is traced in Arize.
Challenges we ran into
- Semantic memory leaked across matchups: a brand-new pairing retrieved another rivalry's fights. We now filter results to facts that name both fighters.
- Sandbox networking: our Daytona org tier initially blocked outbound calls to SurrealDB; after an upgrade, stale environment placeholders broke auth until we made
.envauthoritative. - Voices that didn't match characters: a voice designed as an "ethereal elf" came out sounding female. We wrote a small pitch analyzer to measure every voice and regenerated until each one matched.
- Noisy traces: framework auto-instrumentation buried the real decisions under untyped HTTP spans; we disabled it and added one meaningful span per turn.
Accomplishments that we're proud of
- Grudges you can actually see: the memory panel, the moves, and the taunts all change based on past fights.
- Five sponsor tools working together in one live loop, end to end, publicly hosted.
- Seven distinct character voices that stay consistent line to line.
What we learned
- Memory is only useful if retrieval is scoped — "similar" isn't the same as "relevant".
- Constraining an agent (strict schemas, server-side rules) makes it more fun to watch, not less.
- Good traces are designed, not just enabled.
What's next for Soul Link
- Measure the effect of memory with controlled matches (with vs. without history).
- Register per-matchup memory scopes so filtering happens in SurrealDB.
- Tournaments and leaderboards where grudges carry across a whole bracket.
- Let players coach their fighter between rounds.
Built With
- anthropic
- arize
- aws-bedrock
- claude
- daytona
- elevenlabs
- fastapi
- javascript
- python
- surrealdb
- vite
- web-audio-api
Log in or sign up for Devpost to join the conversation.