Building Bill Orbit: A Multiplayer Receipt Splitter for the Browser.doc

Building Bill Orbit: A Multiplayer Receipt Splitter for the Browser

23 August 2026 Harry Chapman

I built a temporary browser-based bill splitter where everyone at the table can scan a receipt, claim items and see the same totals without creating an account.

I built a browser-based bill splitter called Bill Orbit. I wanted a straightforward, temporary web app for restaurant bills where everyone at the table could claim their own food and drinks at the same time.

Most of the tools I found required an app download, a user account or one person to type every item into a calculator while everyone else shouted across the table. I wanted a URL that could go into a group chat, put one shared receipt on every screen and delete the room the next morning.

I also wanted it to be enjoyable to use. Splitting a bill is administrative work at the end of a nice dinner. The interface did not need to make light of the money, but it could make the interaction less tedious. That is why Bill Orbit uses customisable plates that follow cursors and eat items dropped onto them.

A shared table, disguised as software

Split the bill. Feed the plates.

One temporary room, one editable receipt and no designated table accountant.

Try Bill Orbit
Bill OrbitThe Wobbly Table

Fish & chips£18.50

2 × House lager£13.00

Garlic bread£6.00

Sticky date pudding£9.50

Total£47.00
£0 baseline cost24 hours before expiryLive shared editsNo account required

The interface layout

The screen is arranged like a tabletop. An itemised receipt sits in the centre, surrounded by circular plates representing the people at the table. Dragging an item from the receipt onto a plate assigns that cost to the person.

Bill Orbit is a single-page application written in vanilla TypeScript. It communicates over WebSockets with a backend running on Cloudflare Workers and Durable Objects.

There are no persistent user accounts. Creating a session generates a random six-character room code and a cryptographic token. Anyone with the link or QR code joins as an equal participant and can edit the bill. The Durable Object holds the live room state, with KV storage used when active changes need to be retained. A scheduled job permanently deletes rooms that have been inactive for more than 24 hours.

Four rules before the feature list

Choose a design principle

Make people the interface

Participants became animated plates rather than names in a spreadsheet. Their eyes follow the action, their mouths react when food arrives and their size grows with their share.

Keeping the operational footprint small was an intentional constraint. The live application runs within Cloudflare’s free allowances, serves its own WOFF2 fonts and uses no external database service. Uploaded receipt photographs are discarded after parsing.

Plate customisation and animation

The plates are expressive avatars rather than static profile pictures. People can choose colours, rim patterns and preset expressions, or draw a face directly in the browser.

While an item is being dragged, the plates’ eyes follow the active cursor. A nearby plate opens its mouth. A successful drop triggers a short squash-and-stretch eating animation before the plate returns to rest.

The receipt and plate layout changes on screens narrower than 768 pixels. The receipt stays anchored while the plates move into a horizontally scrolling tray at the bottom. Shadows at the edge of the tray indicate when more people are off-screen.

Splitting multi-quantity items

Receipt parsers often treat a quantity line as one block. If a receipt says 2 × House Lager £13.00, that usually means assigning the entire £13.00 to one person or manually creating two new lines.

Bill Orbit splits the row into individual item pills. Each pill carries its share of the printed price and can be dragged independently, while the original receipt line and total remain intact.

Try the core interaction

Two beers should look like two beers

2 × House lager£13.00

Choose one of the two drinks, then choose a plate.

This lets two people claim one beer each without editing the receipt first.

Keeping animated plates from moving their hitboxes

An early phone build used the animated SVG boundary as the drop target. When a plate squashed or stretched, its collision boundary moved under the person’s thumb. With two plates close together, the expanding edge of one could capture an item intended for the other.

I separated the visual plate from the collision area. The hitbox is now an invisible circle with a fixed radius. The SVG plate sits over it and can scale, lean and track the cursor without changing where a drop is detected.

Real-time room state

Everyone in a room can edit the same receipt at once, including on an unreliable mobile connection. The server therefore owns the authoritative state.

When a person claims an item, their browser updates immediately and sends an ASSIGN_ITEM action containing the item, quantity segment and target person. The Durable Object processes actions sequentially. If somebody else has already claimed that segment, the server rejects the later action and sends the current state back to that browser.

Live cursors use the same WebSocket connection but do not enter the persistent state machine. Their coordinates are sent as a small Uint8Array and throttled to 30 updates per second for each client. They disappear when the connection closes and are never written into the room history.

Receipt scanning and the Berlin receipt

The scanning flow accepts a camera photograph or uploaded file and extracts item names, quantities and prices. On desktop hardware it can run locally through Tesseract.js and WebAssembly. Lower-powered mobile devices can use a serverless vision endpoint instead.

Early versions worked on tidy UK receipts and then failed on a restaurant receipt from Berlin:

2 x Radeberger Pils A          12,00
1 x Vodka White Edition B       9,20
1 x Blaubeer Mojito B          11,00
2 x Schnitzel Klassik A        28,60
 
MWST A 19,00%                  10,07
MWST B  7,00%                   2,01
 
SUMME EUR                     136,10

The parser assumed decimal points, so it mishandled comma prices. The A and B tax categories contaminated item names and prices. Tax summary rows also looked enough like line items to be added to the food order.

I added comma normalisation before prices are converted into integer minor units. Lines containing tax markers such as MWST, TVA, IVA, TAX or VAT alongside a percentage are held out of the item list.

ALEX GASTSTÄTTEN · BERLINRECHNUNG

2 × Radeberger Pils12,00

Vodka White Edition9,20

Blaubeer Mojito11,00

Tequila Sunrise10,10

Super Salad12,50

Schnitzel Klassik28,60

UNBAR TOTAL136,10 EUR

Recognition should fail helpfully

The useful lines vanished with the error

The first version found useful items, noticed that their prices did not match the printed total, then discarded everything.

Partial result hiddenThe person sees an error and has to begin again.

The first parser was also all-or-nothing. If the extracted items did not add up to the printed total, it returned a generic error and discarded every recognised line.

The current flow keeps partial results. It draws boxes around the lines it recognised and shows the difference between their sum and the printed total. The person can tap an unrecognised area of the photograph to add the missing item instead of typing the whole receipt again.

Bugs that changed the implementation

A price field stopped halfway through typing

An early validation rule prevented a line from exceeding the unassigned balance. If £5.00 remained and somebody tried to correct a line to £12.00, the input rejected the temporary value as soon as they typed 12.

Price fields now keep the unfinished string while somebody is typing. The app sanitises, clamps and broadcasts the value only on blur or when Enter is pressed.

Tips made plate totals look wrong

Tips can be split evenly or in proportion to what each person ordered. During testing, a plate might display £23.50 even though its visible items added up to £20.00.

The plate now separates the amounts, for example £20.00 items + £3.50 tip, before showing the final total.

Uneven divisions lost a penny

All prices are converted to whole pennies or cents on input. This avoids the floating-point errors that come from using JavaScript numbers for currency.

An uneven split still needs an explicit remainder rule. Dividing £10.00 between three people produces a base share of 333p and one spare penny. Bill Orbit assigns the remainder to participants in index order, producing 334p, 333p and 333p. The individual shares therefore still add up to exactly £10.00.

Custom faces became blurry

The first face editor used an HTML canvas. Drawings looked sharp in the setup window but became soft when the plate was reduced for the table or displayed on a high-density phone screen.

The current editor stores pen strokes as normalised vector coordinates and renders them as inline SVG paths. The same drawing remains sharp at the full editor size, on a plate and in the compact mobile tray.

Verification

Automated unit and end-to-end tests cover the arithmetic and shared room state. I also test the interface with real receipt shapes and deliberately awkward display settings.

Test areaCasesExpected result
CurrencyUneven divisions across 3, 7 and 11 peopleIndividual shares equal the receipt total exactly
ReconnectionDisconnects and reconnects under a throttled connectionThe browser restores state without duplicating assignments
Receipt parsingSingle-column, multi-column and European comma receiptsItems are extracted while tax blocks are excluded
Concurrent dropsTwo people claim the same item in one state tickThe first claim is retained and the later browser is corrected
Display scaling200% zoom and mobile layout changesPlates remain readable and drop targets do not overlap

Seven rounds of iteration

The route from useful to alive

Stage 1 of 7 · Idea

A shared tabletop, not a dashboard

Put one receipt in the centre and make the people around it the controls.

Current status

The live app supports receipt scanning, manual entry, item assignment, custom plate faces, real-time cursors and proportional tip splitting. It does not require an account, and inactive rooms are deleted after 24 hours.

The next work is broader regional receipt support, PDF receipt parsing and a smaller initial WebAssembly download for mobile devices.

The table is open

Scan a receipt, invite some friends and feed the plates.

No account is needed, receipt photographs are not stored and each room disappears after 24 hours.

Open Bill Orbit ↗
Page 1 1,445 words 7 min Play