Batchlo

Notes from the v1 launch

Batchlo is production ops software for indie hardware makers. It tracks filament, components, and printer status in real time, so you know what you can build before you take an order. Desktop app, connects to Bambu printers in LAN mode, replaces the spreadsheet most one-person hardware businesses run on.

I built it solo. These are notes on the decisions behind v1.

Batchlo home screen showing build capacity grid and live printer status.

The shape of the problem

Most indie hardware makers I've talked to run their BOM in a spreadsheet. Their printer is on the desk. The connection between the two is memory. Take an order, start the print, run out of M3 brass inserts halfway through. Tell the customer next week.

Existing tools are mostly built for shops with employees. Craftybase scales up to $349 across five tiers. Printavo runs $49 to $249. Katana starts at $299. They assume an ERP and a team.

I wanted one window. Knows what's printing. Knows what's in stock. Tells you what you can build right now. No cloud account for the printer. No five-tier maze. No week of setup.

Spool architecture, deferred

Spool data in Batchlo is fragmented. Each AMS slot has five separate objects tracking it: grams override, weight override, slot name, price override, spool memory. Inactive spools live in a separate filaments[] array. Two different shapes for the same physical thing, a roll of filament that moves between the printer and a shelf.

I designed the unified version. One spools[] array. Each spool has a location field, either ams or inventory, plus a slotIdx when loaded. Swap becomes a state change: flip the location, set or clear the slot index. Migration logic written, schema drafted.

I didn't ship it.

The unified model touches every render function in the app. Every read of filament data needs updating. Pre-launch, with v1 stable and tested, the regression risk wasn't worth a cleaner data shape. It's queued for v1.1, behind a one-time migration. On load, if the old shape exists and the new one doesn't, fold both arrays in and bump the storage key.

The current architecture is uglier than I want. It works.

AMS slot UI mid-swap, or the inventory view.

Pricing

The pricing page took longer than several features.

I started with two tiers. Solo at €19. Studio at €39. Clean, but without an anchor, both numbers feel arbitrary. €19 reads as cheap or as suspicious. €39 reads as a small bump for not much extra.

I went back to the competitive set. Craftybase ranges from $24 to $349 across five tiers, gating on order volume and integrations. Printavo tops at $249. Katana has one tier at $299. The pattern across the category is consistent: seats, volume, and integrations as the gates.

I added a third tier. Pro at €99, unlimited printers and users, API access. Studio bumped from €39 to €49 to match Craftybase's mid-tier and reflect the multi-printer value I'm pricing in.

Pro is there to anchor. With it on the page, Studio reads reasonable and Solo reads cheap. Both Studio and Pro are marked “coming soon,” which is honest about the build state and creates a low-pressure waitlist signal for which tier matters.

Open question: Solo at €19 might be too low. I'll revisit at 50 paying users.

Batchlo pricing page.

When to do nothing

Bambu's RFID-tagged spools auto-detect on swap. Weight, color, brand, all from the printer. Third-party spools have no RFID. The user has to set the filament type and color on the printer before MQTT fires and Batchlo can detect anything.

Users load a third-party spool, expect it to show in Batchlo, and nothing happens until they tell the printer what it is.

The obvious fix is an in-app prompt. Detected an unknown spool, set it up here. I considered it. I'm not building it.

My users run small hardware businesses, they probably know their printer. Bambu's RFID system not recognizing third-party rolls is a known limitation in that world, most users will have a workflow for it already. The deeper issue is that if I patch it in my app, I own the workaround forever. Every Bambu firmware update becomes a question of whether my fix still holds.

Note for later: revisit if it becomes a consistent support complaint. So far, none.

The same principle drove a paired decision. When a Bambu RFID spool is detected on swap, the swap popup is suppressed. Weight, brand, and remaining grams come from the MQTT payload, applied silently. Third-party spools, where the data isn't trustworthy, still trigger the popup. Interrupt the user only when the interruption helps.

Smaller things

A few decisions worth mentioning briefly.

Contrast bump. The --light CSS variable was #444, roughly 1.8:1 against the dark background. Effectively invisible outside perfect lighting. Bumped to #686868 (3.3:1) in one variable change. Propagated across every label, hint, sublabel, and inactive tab. Should have caught it earlier.

Modal reuse. The “new spool” button in the swap popup opens the existing add-spool modal, pre-filled with whatever the printer reported. Same modal, different entry point. Less code, consistent UX.

GitHub releases. Each build uploaded to a private releases repo with a stable URL per version. v0.1.0 and v0.9.0 both still live. If a customer hits a regression, I can roll them back in one link.

What's next

Unified spools migration goes into v1.1, behind the storage version bump. Studio tier features (queue planner, multi-user) are next on the build list, gated by waitlist signups. Not building Pro until someone asks.

The intake form and the order kanban are the two pieces I'm watching closest post-launch. They're where Batchlo stops being a tracker and starts being an operations tool. Whether they get used heavily or barely will tell me what Batchlo actually is.

Batchlo is live at batchlo.com. Questions or feedback: max@skogsdue.com.