CatalogPersonal life ›Wellbeing

Period Tracker — private cycle calendar

A minimal diary for your menstrual cycle: log the start date and its length, and the app works out on its own when the next one is due. Unlike several well-known apps in this space that have faced real privacy controversies, here your data stays **only on your device** — no account, no server, no syncing back to any company: your cycle is some of the most personal information there is, and it never leaves your phone.

▶ Use this app

period trackercyclecalendarhealthprivacywellbeing

What it records

  • Cycle length in days, e.g. 28

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
# 🩸 Period Tracker

A minimal diary for your menstrual cycle: log the start date and its length,
and the app works out on its own when the next one is due. Unlike several
well-known apps in this space that have faced real privacy controversies,
here your data stays **only on your device** — no account, no server, no
syncing back to any company: your cycle is some of the most personal
information there is, and it never leaves your phone.

::::form{path="cycles" id="fci"}
::input{form="fci" field="start" type="date" required="true" legend="Cycle start"}
::input{form="fci" field="length" type="number" placeholder="Cycle length in days, e.g. 28" required="true"}
::add-form{form="fci" path="cycles" label="Log cycle"}
::::

## History

:::sort{path="cycles" field="start" order="desc" as="table" headers="Start,Length (days)" deletable="true" editform="fci"}
{start} | {length}
:::

:::python{data="cycles"}
```
from datetime import date, timedelta

rows = data["cycles"]
if rows:
    ordered = sorted(rows, key=lambda r: r.get("start", ""))
    last = ordered[-1]
    length = int(last.get("length") or 28)
    start = date.fromisoformat(last["start"])
    next_date = start + timedelta(days=length)
    print(f"Next period expected: {next_date.strftime('%b %d, %Y')}")
else:
    print("Log at least one cycle to see the prediction.")
```
:::

Every time you add a new cycle the prediction recalculates on its own from
the latest entry — just keep the history above up to date.