FennecXR

Historic Tunisia Reimagined

Inspiration

TanitXR has volunteers scanning at-risk Tunisian heritage sites and posting the results to Sketchfab. We pulled the full archive and checked it: every single record had empty tags, empty categories, and no location data a machine could read. A capital from Byrsa Hill and a mihrab from Medersa Slimanya sit next to each other in the same feed with nothing connecting either one to where it actually stood. The scans preserve an object's shape. They throw away its place. We wanted to put the place back.

What it does

FennecXR is a browser-based, low-poly walkable reconstruction of Carthage's real find-sites, the Byrsa Plateau, the Tophet, the Baths of Antoninus, built with real TanitXR photogrammetry scans standing where archaeologists actually found them. A separate study court holds Kairouan, Tunis, and Zaghouan material, labeled with its real source so nothing is misrepresented as Carthage.

Walk with WASD or the arrow keys, sprint with Shift, press E near any object to open its record: what it is, who scanned it, what license it carries, pulled straight from TanitXR's own documentation. Nothing is paraphrased or invented. If a record has no description on file, it says so instead of filling the gap. Press G and every modern reconstruction, terrain, walls, columns, plinths, disappears, leaving only the real scanned geometry, so you can never mistake our guesswork for the archive's fact.

The current build holds 21 records. Sixteen have a real scan integrated. The other five stand as plain grey stone proxies rather than something faked to look finished.

How we built it

React Three Fiber on top of Three.js for the world. We pulled the archive through Sketchfab's public API, no auth needed for read access, and audited every record programmatically before writing a line of scene code: 99 total models, zero tags on all of them, a mix of duplicate uploads (some genuine reuploads, some just different scans of visibly different stelae with identical names), and site names getting truncated mid-word at the API's 48-character description cutoff, which broke our first pass at automatic zone assignment.

We ran two builds in parallel through most of the hack and merged partway through: one teammate's rendering held up better under real lighting, so that became the base, and the data pipeline and audit findings from the other build got folded into it rather than thrown out. Sixteen GLBs came down manually through the Sketchfab UI, since bulk download needs OAuth we didn't have time to set up, then got compressed locally with WebP textures and meshopt, which took the browser payload from 56.6MB to 7.1MB.

For QA we pointed a Playwright MCP browser at the running build and had it walk the scene taking screenshots from inside each zone.

Challenges we ran into

The metadata problem was worse than the challenge brief implied. It called out two duplicate Corinthian capitals. The real pattern was 62 records sharing a name at different triangle counts, mostly decimated variants of the same object, but eight separate records all named "Punic Stelae, Tophet of Salammbo" that are visibly different stones. We couldn't merge on name alone without deleting real objects, so the interface flags a possible duplicate without collapsing it, and says which signal fired.

The Playwright pass caught bugs we wouldn't have found by eye. Drei's Html component assigns a default z-index over 16 million, which meant a zone plaque was rendering directly on top of the examine panel's text. All four zone plaques were also rendering simultaneously through walls from anywhere on the map, not just the one you were standing in.

Accomplishments that we're proud of

Every fact shown in the app traces back to TanitXR's own words. We built the empty-record state on purpose: rather than write plausible-sounding filler for the objects with thin documentation, it tells you outright that the scan exists but its history hasn't been recorded yet. That's not a placeholder we forgot to finish, it's a deliberate line between what the archive actually knows and what it doesn't, which is the whole point of the project.

We also caught our own license handling before it became a problem instead of after. The Corinthian capital carries a Creative Commons Attribution-NonCommercial-ShareAlike license, different from the plain Attribution license on other models in the archive, credited by name to the volunteer who scanned it and the person who optimized it for real-time use. Rather than assume one blanket license across everything, each model's license is tracked individually and shown per-record.

What we learned

A raw archive and an organized archive are two different projects. TanitXR did the hard part, getting a camera to sixteen sites and processing the scans, and the gap left over wasn't more scanning, it was structure: place, time, relationship between objects. That gap is closeable in a day if you're honest about what you don't know yet rather than papering over it.

What's next for FennecXR

Push the tag and site data we extracted back into Sketchfab itself, so the archive is filterable at the source and not just inside our app. Extend past the current 21-record subset to the rest of the archive. Add spoken narration on top of the Gemini guide, so a spoken interest, "I like architecture," gets a spoken answer back instead of just a filtered list.

Share this project:

Updates

Submission history