-
-
welcome screen
-
show puzzle on welcome screen
-
puzzle gameplay
-
puzzle gameplay filter chips
-
puzzle gameplay another hint
-
puzzle gameplay solved
-
puzzle gameplay show answer
-
puzzle gameplay answer reveal
-
open puzzle options inside app
-
move recenter when app opens
-
press recenter inside app
-
puzzle gameplay solved with 3 hints
Inspiration
I work with data, and I have also helped students learn SQL. Joins are where many people get stuck. On a screen, a join is just text, and it is hard to see why a customer shows up twice or why a row disappears. I wanted to make joins something you can hold and watch happen.
What it does
SpatialQL is a mixed reality puzzle app for Meta Quest. Two tables appear in front of you at your desk. You pinch one, bring it to the other, and let go. Matching rows snap together, color coded by key, and fly into a result table with the SQL query written above it.
There are 20 puzzles across four small datasets: a shop, a library, a school and a cafe. They teach one idea at a time: inner joins, LEFT joins, which table to keep, rows that repeat, and filtering on NULL. If your answer is not right yet, the app shows which rows are missing or extra instead of saying "wrong". There is a daily puzzle, a streak with a calendar, hints, and a timer that shows how long each puzzle took.
It is meant as a short daily habit: a few minutes a day to get better at a skill people use at work.
How we built it
I built it on my own with Meta's Immersive Web SDK, which runs on WebXR in the Quest Browser. The code is TypeScript with Vite, the tests use Vitest, and it is hosted on Vercel. The join logic and the puzzles live in a separate data layer, with tests that check every puzzle can be solved and that each wrong move gives the expected result. I used Claude Code as a coding assistant throughout, and wrote detailed specs and reviewed each change. I tested on a rented Quest 3S, with logs from each headset session saved to my laptop so I could see exactly what happened.
Challenges we ran into
Hand tracking was the hardest part. Relaxed hands sometimes looked like pinches, so buttons pressed on their own. Fixing that without making real pinches harder took many rounds of reading logs and adjusting.
Layout was another. Things that looked fine in the emulator were too low, too close or out of reach on the real headset. Seated players hold their heads almost level, so I had to rework the layout around that.
I also found a bug where the app placed everything before the headset had finished setting up its tracking, so the tables appeared far away and facing the wrong way.
Accomplishments that we're proud of
This is my first VR app, and the first time I have used a VR headset. I am happy with how it grew from a basic free play sandbox into a structured app with 20 puzzles, a daily streak and a full set of hand controls, and that it looks and feels polished.
The join itself feels good. Bringing two tables together and watching the matching rows connect makes the idea click. I am also proud that every puzzle is checked by tests, and that the app runs with no controllers at all.
What we learned
Test on real hardware as early as possible. The emulator cannot tell you how a pinch feels or where your eyes naturally rest. Logging every headset session saved me a lot of guessing.
What's next for SpatialQL
A new generated puzzle every day, more join types like RIGHT and FULL, joins across three tables, and GROUP BY. I would also like to let people load their own small CSV files and explore their own data.
I also want to add a voice tutor. Instead of reading hints, you could just ask out loud, like "why is this customer missing?", and an AI expert would look at your tables and your current result and guide you toward the answer without giving it away. It would feel more like having a patient teacher sitting next to you.
Built With
- claude-code
- hand-tracking
- immersive-web-sdk
- lucide
- meta-quest
- mixed-reality
- node.js
- passthrough
- pmndrs-uikit
- quest-browser
- scene-understanding
- spatial-audio
- sql
- three.js
- typescript
- vercel
- vite
- vitest
- web-audio-api
- webxr
Log in or sign up for Devpost to join the conversation.