Splitting bills during group activities seems simple — until it isn’t.
A trip, dinner with friends, shared groceries, or roommate utilities can quickly turn into:
“Wait… who paid for what?”
“Didn’t I cover the last two things?”
“Why am I paying more?”
What we need isn’t a “wallet” app or budgeting suite — just a simple, transparent way to record shared expenses and settle up fairly.
In this post, we’ll walk through how to build a minimal and practical bill-splitting app, similar to the one at:
👉 billsplitonline.com
(live working demo, no login required)
The focus is:
Simplicity, usability, and clarity.
No accounts.
No friction.
No “invite your friends” onboarding.
Just open → add expenses → get settlement results.
Core Problem to Solve
The fair way to split group expenses is:
Track who paid → Track who participated → Calculate who owes who at the end.
We maintain:
people = { Alice, Bob, Charlie }
expenses = [
{ whoPaid: "Alice", amount: 40, sharedBy: ["Alice", "Bob"] },
{ whoPaid: "Bob", amount: 60, sharedBy: ["Bob", "Charlie"] },
{ whoPaid: "Charlie", amount: 30, sharedBy: ["Alice", "Charlie"] }
]
From there we compute:
- Each person’s total paid
- Each person’s fair share
- The net balance (positive = owed money, negative = owes others)
Settlement Algorithm (Minimal Version)
function settleUp(people, expenses) {
const balance = {};
// initialize balance
people.forEach(p => balance[p] = 0);
// compute costs
expenses.forEach(e => {
const splitAmount = e.amount / e.sharedBy.length;
e.sharedBy.forEach(p => balance[p] -= splitAmount); // each owes their share
balance[e.whoPaid] += e.amount; // payer gets credit
});
// convert to payers and receivers
const owes = [];
const owed = [];
for (const p in balance) {
if (balance[p] < 0) owes.push({ person: p, amount: -balance[p] });
if (balance[p] > 0) owed.push({ person: p, amount: balance[p] });
}
const settlements = [];
// match payers to receivers
while (owes.length && owed.length) {
const o = owes[0];
const r = owed[0];
const amount = Math.min(o.amount, r.amount);
settlements.push(`${o.person} pays ${r.person} $${amount.toFixed(2)}`);
o.amount -= amount;
r.amount -= amount;
if (o.amount === 0) owes.shift();
if (r.amount === 0) owed.shift();
}
return settlements;
}
This produces clean human-friendly results like:
Bob pays Alice $20.00
Charlie pays Bob $15.00
Live Example of Bill Splitting App
It’s built following the exact principles discussed in this post:
- No login
- Works offline
- Clear settlement output
- Mobile-first UI
- Uses the algorithm above
Feel free to inspect the network requests — there aren’t any.
Everything runs client-side.
Final Thoughts
Sometimes we overcomplicate software.
Not every app needs:
- OAuth
- Microservices
- IndexedDB
- Multi-tenant SaaS architecture
Sometimes the most useful tools are the ones that just work quickly with zero cognitive load.
A small, thoughtful UI + a transparent algorithm solves a real social problem here:
money awkwardness among friends.
Top comments (0)