◉RORK LABJP
●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SONNET 4.5 — Retires on the Claude API on 11/30, 26 days left. If your app calls it, check the migration target●SDK 58 — Moves to React Native 0.88. R8 is now on by default in Android release builds, and iOS prebuild generates a SceneDelegate●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SONNET 4.5 — Retires on the Claude API on 11/30, 26 days left. If your app calls it, check the migration target●SDK 58 — Moves to React Native 0.88. R8 is now on by default in Android release builds, and iOS prebuild generates a SceneDelegate●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…
Articles/AI Models
◉ AI Models/2026-06-15Advanced

Building a Room-Measuring App with Rork Max and RoomPlan — Absorbing Scan Variance by Design

Now that Rork Max generates native Swift, a LiDAR-based RoomPlan scanning app is a realistic project for an indie developer. Scan results drift depending on device and how you hold it. This walks through how to absorb that drift in your app design and export stable dimensions.

Rork Max235RoomPlanLiDARSwift48Architecture23

✦ Premium Article

The first time I used RoomPlan, I scanned the same small room three times and got three different wall lengths: 3.62m, 3.57m, 3.64m. A few centimeters of difference, but if you are building an app that draws a floor plan, you cannot expose that drift to the user. People do not trust a tool whose numbers change every time they measure.

Now that Rork Max can generate native Swift, a LiDAR-based measuring app suddenly came within reach for indie developers. But ship the generated code as-is and that drift lands directly in the user's hands. Here I share how I learned to absorb scan variance by design, working through a small measuring app of my own.

Why RoomPlan numbers drift

RoomPlan sits on top of ARKit. It combines the LiDAR point cloud with planes estimated from the camera image to reconstruct the room's structure. In other words, the final dimensions are not a measurement but an estimate. Because it is an estimate, the result moves with the input conditions.

There are three main sources of drift. Lighting: against backlight or in the dark, feature points are scarce and plane estimation gets loose. Motion speed: sweep too fast and the point cloud thins out, delaying how edges are resolved. And the device itself: iPhone and iPad Pro differ in LiDAR resolution and field of view, so the same room reconstructs slightly differently.

The important thing is to not try to crush these as bugs. Estimation drift is the spec, and designing your app to absorb it produces a more stable result in the end.

A three-layer design that absorbs drift

I treat measurement data in three layers: raw, confirmed, and display.

The raw layer keeps the CapturedRoom that RoomPlan returns, untouched. Nothing is rounded here. The confirmed layer applies rounding and snapping to turn raw data into "dimensions the app takes responsibility for." The display layer shows the user only the confirmed values.

With this separation, when you later want to change the rounding rules, you can redo it without throwing away the raw data. Scanning is an effort for the user, so a design that reduces re-scans directly improves the experience.

Rounding and snapping in the confirmed layer

The confirmed layer has two core operations. One is rounding to 5cm units. The other is snapping toward right angles and parallels. Real room walls are made almost entirely of right angles and parallels, so if the estimate returns 88 or 92 degrees, nudging it to 90 produces a more natural drawing.

import RoomPlan
import simd
 
struct ConfirmedDimension {
    let widthMeters: Double
    let lengthMeters: Double
    let cornerAngles: [Double]   // each corner angle, in degrees
}
 
enum DimensionResolver {
    // Round to 5cm so users see a granularity they can trust
    static func snapLength(_ raw: Double) -> Double {
        let grid = 0.05
        return (raw / grid).rounded() * grid
    }
 
    // Snap toward right angles. Within +/-4 degrees, pull to a multiple of 90
    static func snapAngle(_ rawDeg: Double) -> Double {
        let nearest = (rawDeg / 90.0).rounded() * 90.0
        return abs(rawDeg - nearest) <= 4.0 ? nearest : rawDeg
    }
 
    static func resolve(from room: CapturedRoom) -> ConfirmedDimension {
        let walls = room.walls
        let lengths = walls.map { Double($0.dimensions.x) }.sorted(by: >)
        let width = snapLength(lengths.first ?? 0)
        let length = snapLength(lengths.dropFirst().first ?? 0)
 
        let angles = walls.map { wall -> Double in
            let yaw = atan2(wall.transform.columns.0.z, wall.transform.columns.0.x)
            return snapAngle(yaw * 180 / .pi)
        }
        return ConfirmedDimension(widthMeters: width, lengthMeters: length, cornerAngles: angles)
    }
}

The +/-4 degrees threshold is a value I settled on after scanning a few times on my own devices. Snap across too wide an angle and rooms with genuinely diagonal walls break. Too narrow and you fail to absorb the drift. Carry this as a number you will tune for the kinds of rooms your app handles.

Carry a confidence value alongside

If you hand over only the rounded numbers, the display layer cannot judge how much to trust them. I attach a confidence score to the confirmed value. Even a rough calculation from point-cloud density and scan time is enough to drive what the display shows.

struct DimensionWithConfidence {
    let dimension: ConfirmedDimension
    let confidence: Double   // 0.0 to 1.0
 
    var needsRescanHint: Bool { confidence < 0.6 }
}

When needsRescanHint is set, the display layer adds "scanning again slowly will improve accuracy." Rather than hiding the numbers, honestly conveying their certainty is what earns the user's trust in the tool.

✦

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
✦A rounding and snapping design that assumes RoomPlan results drift 5-10cm by device, lighting, and hand motion, so you can lock in trustworthy dimensions
✦How to add a minimal shared-contract layer that exports dimensions to JSON on top of the Swift code Rork Max generates
✦Judgment criteria for a LiDAR-absent fallback that survives App Store review instead of getting rejected
Secure payment via Stripe · Cancel anytime
✦

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

Related Articles

◉ AI Models2026-09-15
Where the AI decision lives once Rork splits your app into two native codebases
New Rork apps are Swift on iPhone and Kotlin on Android — two separate codebases. Here is how I decide which AI-related judgments stay in each client and which move to the shared backend, plus the minimal contract both clients read the same way.
◉ AI Models2026-09-12
Ask Rork for an iOS 27 feature and you may get last year's code back, with no error at all
iOS 27 ships on September 14. When a model is asked for an API it has never seen, there are two kinds of failure: the kind that stops your build, and the kind that does not. Here is why standard Rork and Rork Max differ, and the order I check generated code in.
◉ AI Models2026-08-07
Cutting MCP Tools Didn't Make Anything Lighter — 2,377 Bytes of Definitions vs 110,298 Bytes of Response
I wrote a minimal MCP server for a wallpaper catalog and measured the byte cost of tool definitions against the byte cost of responses. Here is which side actually matters, and the real reason to merge tools.