CatalogTools ›Everyday

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.

▶ Use this app

expensessplit billgroup triproommates

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.

  1. Use it right away. Hit “Use this app”: it opens right in your browser, ready to go, with data saved only on your device.
  2. Your data stays yours. Everything is stored locally on your device, kept separate for each app: no account, no server, nothing to set up.
  3. Make it your own. The text below is the entire app: copy it, tweak fields, views and words, and your changes become interactive instantly.
  4. 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.