t15n · Thibaut Tiberghien
Frontier models · 13 August 2026 · 11 min read

The rewrite we avoided for four years

When Anthropic released Fable 5¹ in June, I wanted to find out whether a frontier model was worth what it costs, and the honest way to find that out is to give it something hard. I picked the oldest problem in our backlog, the one nobody had wanted to start: the way Smplrspace's 3D viewer rebuilds an entire building every time you look at a different floor, which on a large one meant waiting 21.6 seconds to move down a single storey.

The problem

Smplrspace gives real estate teams interactive floor plans and 3D views of their buildings. The viewer uses Babylon.js to draw them in a browser, floor by floor, with walls, doors, windows and furniture in 3D. A large one runs to dozens of floors, thousands of pieces of furniture and well over a million triangles. The viewer normally builds the whole thing up front, every floor and everything inside it, and after that, moving between floors is only a matter of hiding and showing geometry that already exists, which is instant. All of the waiting is concentrated in that first build, which on a large and complex building can run to twenty seconds or more.

Most of a building's geometry is interior walls, and you only ever see inside the highest floor on display, since everything below it sits under the floor above. That observation became a second rendering mode, which we shipped behind an experimental flag in 2024. Instead of building everything, it builds only what you can actually see: the shell of the building, its facade, and the full interior of that top floor. First paint gets much cheaper, on larger buildings something like 3.2 seconds down to 0.6, since on a fifteen-floor building it skips fourteen floors of walls nobody is looking at.

The catch is that moving down a floor changes what needs to exist, and the renderer only knew how to build a whole scene at once. So every floor change threw everything away and built the entire visible set again from nothing. That 21.6 seconds came from a fifteen-floor building we keep around for performance work. At about 5.5 megabytes of floor plan data it is far larger than the spaces we normally deal with, which is exactly what makes it useful. The mode traded one long wait at the start for a long wait on every floor change, which is why it stayed experimental rather than becoming the default.

A four-year-old idea

The idea for how to fix it goes back to October 2022, and a conversation with Romain Endelin, a colleague at the time. The viewer rebuilt the whole scene whenever a building's data changed, which was far simpler code and comfortably fast enough at the size of the spaces we had then. Explaining that to him is what made the alternative obvious: our editor already held its data in MobX State Tree (MST), a library that keeps state as a tree of typed objects and can tell you what changed between two versions of it. If the viewer held its state the same way, the diffing would come almost for free, and we could reconcile the scene instead of rebuilding it.

Where we disagreed was on how to get that diff. Romain had tried MST's change-tracking API and found it awkward for what we needed, since the change records were loosely typed and identified their target by string paths, so he wrote a reconciliation by hand instead and had it working in a side project. I thought building our own diff was a lot of effort to replace something we were meant to get for nothing.

The file that turned a floor plan into a 3D scene was the oldest and the largest piece of JavaScript left in our codebase, and everything else in the viewer leaned on it, so rewriting it meant many weeks of rebuilding a good part of the viewer's mechanics from scratch, with a high risk of breakage along the way. That kind of work is exciting but hard to schedule. You can see what could be done and how the product would be better for it, but the effort is not worth the impact at that point, and there is always something that is. Leaving aside an interesting piece of work that you know how to solve is one of the harder calls in building a product, and I kept making it for four years.

Putting Fable on it

It was still not a priority in 2026, but I wanted to know what Fable 5 could actually do. The model is priced well above what I reach for day to day, and I would rather form a view firsthand than from benchmarks and other people's reviews. This was one of the hardest problems we had, which made it a good test. It started with close to three hours of planning with Fable, working through the codebase, the map viewer we had built in that same MST style back in 2025 as a working model to follow, and the 2022 conversation itself. There was plenty of real context to draw on, and the session produced a plan with tests aimed at the places it could go wrong.

Cheaper models then carried it out, Opus and Sonnet depending on how mechanical each change looked. That did not work. The rendering came out buggy in ways that were hard to unpick, and some of the goals seem to have been misunderstood along the way, so for the second attempt I had Fable do the execution as well, working from the same plan in a fresh session with no knowledge of the earlier one.

The work moved the viewer's state into MST and built the diffing on top of it. The plan had one open question in it from the start: whether MST's change-tracking API, the part Romain had set aside in 2022, would actually deliver the diffing. Fable's answer was no: routing every change through MST would land its overhead in the hottest part of the render loop, and its change records still identified what had changed by position rather than by identity, the same problem Romain had run into four years earlier. What we built instead works from plain snapshots of the data.

How it works

The mechanism is a registry of what has already been built. When a floor's data changes, we compare the incoming walls, grounds (the floor surfaces themselves), windows, doors, roofs and stairs against that registry one item at a time, matched by a stable id. Anything unchanged keeps the geometry it already has. Anything added, removed or genuinely modified marks its merge group² dirty, and only dirty groups get rebuilt.

One property of the data made the comparison cheap. The snapshots of a building we get from MST share memory for anything that has not changed. If a piece of data is the same object in memory as it was last time, nothing underneath it can have changed, so there is no need to compare its contents at all. That single check does most of the work before any real comparison starts.

It also runs after classification rather than on the raw floor plan. The renderer computes each wall's facade or interior status and its material first, and the comparison happens on that result. Edits in a building cascade: marking part of a floor as outdoor space flips the walls along it from interior to facade, which moves each of them into a different group, so one small ground edit can reach dozens of walls that were never touched. Because the comparison sees the classified result, those cascades arrive already resolved. Nothing has to know that editing a ground can reach a wall, so there is no dependency tracking to get wrong.

Until now, every rebuild had waited behind a fixed 300 millisecond delay, there to absorb bursts of edits. Once a rebuild itself took a few dozen milliseconds, that delay was the slowest part of the interaction. So the renderer now estimates what a rebuild will cost before running it. Cheap ones run immediately, with no delay and no loading indicator. Expensive ones show the indicator first, so the browser gets a chance to paint it before the rendering blocks the page. That cutoff sits below Nielsen's one-second response-time limit³, the point past which an interface stops feeling immediate, improving the experience through perceived performance and not just measured performance.

The outcome

The target was the experimental facade mode, where every floor change meant rebuilding the whole scene. On that fifteen-floor building, moving down a floor went from 21.6 seconds to 44 milliseconds the first time you visit it, and to about a millisecond on every visit after, since a floor's geometry stays cached once it has been built, hidden rather than discarded. The tradeoff that had kept facade mode experimental for over two years is gone, and it is the default renderer now. The editor benefited too: editing a single wall on a complex floor (~200 walls) went from a full floor rebuild, between 0.7 and 1.2 seconds, down to 269 milliseconds, because only that wall's merge group has to be rebuilt.

What none of this touches is how long a large building takes to load and render for the first time. The plan there is to have the backend break a space into pieces the viewer can stream and render in chunks, so the first floors appear early and the rest fills in behind them. The diffing is what makes that possible, since the renderer can now add to a scene instead of rebuilding it. We are planning that work now, and will likely put Fable on it again. I would rather it did not take another four years (・_・;)

What it was worth

Fable 5 is expensive, and on this it earned the cost. The restructure ran to 106 commits across 15 pull requests over about two weeks, with that oldest file going from about 4,000 lines to 1,200 and the rest of it becoming a typed store and nine React hooks.

The process was interactive, and I reviewed commit by commit after every pull request, as I would on any change this size. My honest read is that Fable could have one-shotted the whole thing if I had wanted to use it that way. Almost every change I asked for was cosmetic, cutting repetition or matching patterns already used elsewhere in the repository, and very little of it was wrong.

A frontier model pays for itself on the problem you have been avoiding for years. After four years of leaving this one aside, having it ship in two weeks was a relief. For anyone building a product, the backlog has items like this: work you know would improve things, interesting to solve, never worth the disruption. A model that carries most of the execution changes that equation. Even looking down the backlog of a fairly complex product, there is only a select handful I would spend it on. This was one of them.


¹ Fable, Opus and Sonnet – Anthropic's Claude models come in tiers of capability and price. Fable 5 is the frontier tier, the most capable and by some distance the most expensive. Opus and Sonnet are cheaper and faster, and they are what nearly all of our work runs on. Anthropic's model overview.

² Merge group – a batch of geometry, say every wall on a floor sharing one material, combined into a single 3D object so the graphics card draws it in one go instead of hundreds of separate pieces.

³ Nielsen's response-time limits – Jakob Nielsen's three thresholds for interface responsiveness: 0.1 seconds feels instantaneous, 1 second keeps a user's flow of thought uninterrupted, and 10 seconds is about the limit of their attention. Response Times: The 3 Important Limits.