Skip to main content
Back to all posts
Sep. 202611 min read

The couch that would not go down the stairs, and the phone that now says so before you buy it

I bought a couch that would not go down my basement stairs. The flight turns ninety degrees on three winder treads, and I found out halfway, with the couch wedged and the wall gouged. Then a flood forced a water heater replacement and two installers brought a 279 litre tank down the same stairs. Two objects, one staircase, nobody measured it either time. Elbow Room Mobile scans a room with an Android phone that has no LiDAR, gives it back as a 3D model you can design, furnish and walk as your Sim, and answers the question no furniture app answers: can the thing actually get in. A three-seat sofa in the example house clears the front door and the hallway, then loses by 2 ft 10 in at the turn. The solver behind that is checked against the published closed form for a zero-width rod, which is the one part of the receipt I did not write the answer to.

RevenueCat ShipatonRevenueCatReact NativeExpoARCoreKotlinAndroidComputational geometryHackathon
I wrote this post for the RevenueCat Shipaton (Sep 2026), and built Elbow Room Mobile for the same submission. Source at github.com/JonathanSolvesProblems/elbow-room-mobile, and the app's own page, privacy policy and terms are at elbowroom.jonathansolvesproblems.com. The Android build is in Google Play closed testing, so installing it means joining the tester list first. #shipaton
The claim in one sentence: the solver that decides whether a piece of furniture can round a stair turn is checked against the published closed form for a zero-width rod, (a^(2/3) + b^(2/3))^(3/2), which is the one part of the receipt I did not write the answer to. What is not claimed: a survey-grade measurement. The scan reads 7 ft 7 in on a ceiling a tape says is 7 ft 9 in, catalogue sizes are marked typical rather than exact until you confirm them, and the app is in closed testing rather than production because Google requires twelve testers to keep it installed for fourteen continuous days first.
Elbow Room Mobile's home screen: scan a room with an Android phone, with no LiDAR required, and plan it in 3D.
Scan your home with an Android phone and plan it in 3D before you spend a dollar on it. No LiDAR, no depth sensor, no account, and nothing you scan leaves the phone.

Two objects, one staircase, nobody measured

I bought a couch that would not go down my basement stairs. The flight turns ninety degrees on three winder treads, with no landing, so there is nowhere to stand the thing up and swing it. I found out halfway, with the couch wedged and the wall already gouged. Somebody had measured the couch. Nobody had measured the corner. Then it happened again in the same house to somebody else: a flood forced a water heater replacement, and two installers brought a 279 litre tank down those same stairs without putting anything on the walls first. The property listing for that house gives all thirteen rooms to the inch and says nothing at all about the staircase, which is the only part of it that ever caused a problem. Every AR furniture app ever shipped answers whether the thing looks good in the room. Not one of them tells you no.

Why nobody can eyeball a winder turn

The corner is the ladder-around-a-corner problem in its rectangle form. For an object of width w passing between corridors of clear width a and b, the length that just touches both outer walls and the inner corner at angle t is a/sin(t) + b/cos(t) minus w/(sin(t) cos(t)). The object fits only if it is shorter than the minimum of that curve, because it has to survive every angle on the way round, not just the one you happen to picture. That minimum sits nowhere near either extreme, which is exactly why intuition fails here: the tightest moment of the turn is somewhere in the middle, at an angle nobody visualises, and the two measurements a person naturally takes (the corridor widths) do not obviously produce it. On top of that sits the choice of which cross-section goes flat and which stands vertical, since you turn a mattress on its side, and how far you can tilt before a soffit stops you. Tilting is not free either, because standing something on end leaves its own thickness behind in plan. `npm run verify` checks the numerical solver against the published closed form for a zero-width rod, (a^(2/3) + b^(2/3))^(3/2), across several corridor pairs, then runs regression fixtures. That closed form is a known result I did not derive, which makes it the one part of this that grades me rather than the other way round.

The fit check: a three-seat sofa clears the front door and the hallway, then loses by 2 ft 10 in at the stair turn, with the plan drawing stopped at the pinch angle.
The check no planner does. This sofa clears the front door and the hallway, then loses by 2 ft 10 in at the stair turn. The drawing stops at the pinch angle, because an animation that completed would be a lie about what happened on those stairs.

A room from a phone with no depth sensor

The phone I built on has no LiDAR and no time-of-flight sensor, so the scan runs on motion tracking, plane detection and raw depth from motion, each pixel carrying its own confidence value. That confidence is the whole story. Textureless paint gives almost none of it, which is why the ceiling was wrong for two weeks: smoothed depth put a 7 ft 9 in ceiling at 5 ft 3 in, every single time. The fix was to throw away everything but confident raw depth, take the top cluster of what survives, and read the ceiling from that. It now reads 7 ft 7 in, two inches under the tape. Two other failures took the same shape. Two rooms seen through an opening came back as one convex hull, which needed cells cut hollow wherever the floor had been seen underneath. Three seat-height planes came back as an eight-foot bed. Walls are snapped to the room's two dominant axes, because a room measured freehand is never quite square and a plan that admits that is harder to read than one that rounds it.

Walking a room with the camera while a meter fills as the floor, walls and ceiling settle, then a review screen showing what the scan found.
Walk the room with the camera. The meter fills as the floor, the walls and the ceiling settle, and the review screen shows what it actually found rather than what it hopes it found.

Design it, then find out what can get to it

The room comes back as a model you can drag, spin and look into, drawn by a software renderer into SVG polygons with faces sorted in painter's order, plus near-plane clipping for the view from inside. There is no game engine anywhere in it. From there: eight whole looks, every floor, wall and ceiling material, and the light. PLAN drops the walls, looks straight down and letters the measured length of every wall. Take a wall out or add one and the materials take-off follows, so you get how much flooring, paint, baseboard and drywall the room now needs. Then furniture, from a catalogue at typical sizes plus anything you have looked up or measured yourself. Drop a piece and it follows your finger, pulls flush and square to the nearest wall, and turns terracotta when it is standing through a wall or through another piece. Select it and the gap from each of its sides to the nearest obstacle is lettered on the floor, terracotta under the 30 inches a person needs to walk past.

Design mode: eight looks, every floor, wall and ceiling material, and a plan view lettering the measured length of every wall.
Eight looks, every material, and a plan view that letters the measured length of each wall. Change the walls and the materials take-off follows, so the room's cost tracks the room rather than a guess.

The part I care about most is quieter than any of that. Every row in the furniture catalogue already carries its route verdict before you pick it, so a sofa reads "does not get in, short 2 ft 10 in" while it is still a list item. That inverts the normal order of the disappointment. In every other planner you fall in love with the piece, place it, admire it, and find out about the staircase when it arrives. Here the catalogue tells you first, and the room keeps a running score of how many of the pieces in it could actually reach it, with the bill sitting against a budget you set. Prices are always yours; the app never fills one in. SHARE sends the room as plain text: each piece, its size, its price, and whether it gets in.

The furnishing catalogue, where every row states whether the piece can get into this room before it is picked, and a placed piece letters its gaps on the floor.
Every catalogue row says whether the piece can get in before you pick it, and a placed piece letters the gap from each side to whatever is nearest. Under 30 inches, the number turns terracotta.

Then walk it at five foot two

PLAY puts a figure in the room you scanned. A pad walks, a drag turns, and walls and furniture stop you, because a room you can collide with teaches you more about a layout than a top-down plan does. MY EYES drops the camera to the figure's own eye height, and that is the feature I would keep if I had to cut the rest: it is the only honest way to find out what a room feels like at five foot two rather than at whatever height the renderer defaults to. The figure itself is deliberately not a blocky avatar. It is an architect's scale-model figure, eight-sided head, tapered torso, walking through a room that was actually measured, because the point is the room and a cartoon would undercut it.

Game mode: a scale-model figure walking the scanned room, seen from behind with the roof removed, or through its own eyes at its own height.
Your Sim walks the room you scanned, seen from behind with the roof off, or through its own eyes. Walls and furniture stop it, so the layout has to actually work rather than just look right from above.
MY SIM: build, height, skin, hair and clothes, or one photo taken with the front camera and read on the phone for three colours.
MY SIM sets build, height, skin, hair and clothes. Or one photo from the front camera, read on the phone for three colours and deleted the same second. Height matters here, because it changes what the room feels like.

A paywall is a promise

Subscriptions run on RevenueCat. The paywall is built in the RevenueCat editor in the app's own palette, the entitlement gates pieces, rooms and product lookup in code rather than in the interface, and the lookup server checks the entitlement with RevenueCat before it spends anything on a search. That last one is the reason the resolver is a server at all: product lookup runs web search behind a strict JSON schema, so a size is looked up and cited rather than recalled, and an API key never ships inside the app bundle where a spend cap cannot be enforced. The line I would repeat to myself on the next one: a paywall is a promise, and every line on it has to map to a gate in the code. When my copy said "free trial" before a trial actually existed, the fix was to change the copy, not to leave it as aspiration and hope nobody tapped it.

The Elbow Room Pro paywall built in the RevenueCat editor: unlimited pieces, every room kept, product lookup by name.
Elbow Room Pro, built in the RevenueCat editor in the app's own palette. Every line on it maps to an entitlement check in code, and the lookup server asks RevenueCat before it spends anything on a search.

What is not claimed

This is not a survey instrument. The scan reads 7 ft 7 in on a ceiling a tape says is 7 ft 9 in, and I would rather print that number than round it away, because two inches is exactly the size of error that decides a tight route. Catalogue sizes are marked as typical for the kind of thing rather than exact for this product until you confirm them, prices are always your own because the app never fills one in, and a room with no measured route says so instead of guessing. Camera frames are processed on device and discarded, measurements live in device storage, and there is no account. The honest state of the release is the last thing worth saying plainly: the app is in Google Play closed testing, not production, because a personal developer account created after November 2023 cannot publish until twelve testers have kept it installed for fourteen continuous days, and at submission that count was one. Google checks that testers actually open the app, so a link in a video is not a tester and an installed phone is not a tester. That clock is the one part of this build no amount of engineering could shorten.

That tester count is the one thing a reader can actually move. Joining puts the app on your phone and starts the fourteen-day clock; it needs an Android device and a Google sign-in, because Google counts the account rather than the install. There is also a longer, looser walkthrough of the same build if the 1:58 demo was too quick:

Related project

Elbow Room Mobile: scan your home with an Android phone, plan it in 3D, then find out what can actually get in

View the project
An agent that will not put words in Canon's mouth: 21 of 22 quotes found word for word in the manufacturer's own documents
Previous post

An agent that will not put words in Canon's mouth: 21 of 22 quotes found word for word in the manufacturer's own documents

When I worked in film, my Canon T5i would miss focus in auto mode and I would switch to manual. Canon's manual explains it on page 100, and I never read it. Compatibility questions like that have published answers that are hard to find and easy to misquote, so Will It Focus reads the manufacturers' own documents and refuses to paraphrase them into quotation marks. Two Sanity Context endpoints, one agent on gpt-5.4-mini: 131 typed records serve the verdicts, a Knowledge Base built from six manufacturer PDFs serves the explanations, and after the model answers, code looks up every quote in the record it cited. Over 20 questions graded by Sigma's, Canon's and Metabones' own tables, 21 of 22 quoted passages are word for word. The first time I ran it on the Knowledge Base alone it put quotation marks around 13 sentences that appear in none of the six documents, which is the whole reason the verbatim text now lives in typed records.

Sep. 202610 min read
Sixteen real calls to pharmacies, zero prices, and the measured reason why: the agent talks over the pharmacist
Next post

Sixteen real calls to pharmacies, zero prices, and the measured reason why: the agent talks over the pharmacist

Pharmacies paid about 43 cents for thirty metformin, a number CMS publishes weekly from invoices. What you pay is different at every counter and published nowhere, so the only way to learn it is to phone. Sticker gives a CALL-E phone agent a drug and a ZIP, reads the federal NPI registry, calls the pharmacies you authorize, and joins every quoted price to CMS NADAC. Sixteen real calls in one Manhattan ZIP produced zero prices and zero refusals. The platform's own event stream showed why: 24 collisions across 15 traced calls where the agent opened its turn while the pharmacist was still speaking. Filed upstream as issue #415, picked up by a maintainer the same day. The contribution merged as PR #404 after five review passes. Zero is a result if you say how you got it.

Sep. 202610 min read