Bill Splitter for Groups — who owes what
A trip with friends, a shared apartment, a big group dinner: who paid for what, and who owes whom? Log each expense with who paid it and the app works out everyone's balance automatically — a free, account-free alternative to the usual bill-splitting apps, with the history saved right on your device.
What it records
- Description, e.g. Dinner, gas, rent
- Amount
- Who paid, e.g. Alex
How it works
This is a Reactive app: a plain text document that your browser turns into a working app — forms, live data, tables, statistics and even charts, with nothing to install or configure.
- Use it right away. Hit “Use this app”: it opens right in your browser, ready to go, with data saved only on your device.
- Your data stays yours. Everything is stored locally on your device, kept separate for each app: no account, no server, nothing to set up.
- Make it your own. The text below is the entire app: copy it, tweak fields, views and words, and your changes become interactive instantly.
- Share it as a file. An app is a simple file: save it, export it or share it with a link; and with encrypted collaboration several people edit the same data in real time.
The app’s source
# 🧾 Bill Splitter for Groups
A trip with friends, a shared apartment, a big group dinner: who paid for
what, and who owes whom? Log each expense with who paid it and the app works
out everyone's balance automatically — a free, account-free alternative to
the usual bill-splitting apps, with the history saved right on your device.
::::form{path="expenses" id="fsp"}
::input{form="fsp" field="description" placeholder="Description, e.g. Dinner, gas, rent" required="true"}
::input{form="fsp" field="amount" type="number" placeholder="Amount" required="true"}
::input{form="fsp" field="paid_by" placeholder="Who paid, e.g. Alex" required="true"}
::add-form{form="fsp" path="expenses" label="Add expense"}
::::
:::python{data="expenses"}
```
rows = data["expenses"]
total = sum(float(r["amount"]) for r in rows) if rows else 0
people = sorted(set(r["paid_by"] for r in rows if r["paid_by"]))
if people:
share = total / len(people)
print(f"Total spent: {total:.2f} — share per person: {share:.2f}\n")
for p in people:
paid = sum(float(r["amount"]) for r in rows if r["paid_by"] == p)
balance = paid - share
if balance >= 0:
print(f"{p}: paid {paid:.2f}, share {share:.2f} → gets back {balance:.2f}")
else:
print(f"{p}: paid {paid:.2f}, share {share:.2f} → owes {-balance:.2f}")
else:
print("Add an expense with who paid to see the summary.")
```
:::
## Expense history
:::table{path="expenses" headers="Description,Amount,Paid by" deletable="true" editform="fsp"}
{description} | {amount} | {paid_by}
:::
Total spent: **:sum[tot]{path="expenses" field="amount"}**
Every expense stays saved on your device, so you can settle up days later,
even after the trip is over.