A simple, distraction-free Pomodoro timer built with Streamlit. No accounts, no ads, no bloat β just a clean timer that keeps you focused.
The Pomodoro Technique is a time management method that breaks work into focused intervals (traditionally 25 minutes) separated by short breaks. Every 4 focus sessions earn a longer break.
flowchart LR
F1["π
Focus 1"] --> SB1["β Short Break 1"]
SB1 --> F2["π
Focus 2"]
F2 --> SB2["β Short Break 2"]
SB2 --> F3["π
Focus 3"]
F3 --> SB3["β Short Break 3"]
SB3 --> F4["π
Focus 4"]
F4 --> LB["πΏ Long Break"]
LB --> F1
classDef note fill:#f9f,stroke:#333,stroke-width:1px;
- β± Accurate timing β wall-clock delta timing, zero drift regardless of Streamlit rerun speed
- π Full cycle management β auto-advances Focus β Short Break β Long Break (every 4th) β repeat
- π΄ Cycle dots β visual indicator of completed, current, and upcoming sessions in the cycle
- π Audio alarm β plays a custom MP3 when any session completes
- βΆ Auto-start β optionally continues to the next session hands-free
- π Live stats β sessions completed, total focus minutes, sets done
- βοΈ Customisable durations β adjust Focus, Short Break, and Long Break lengths
- π³ Docker ready β ships with a Dockerfile and Compose file
.
βββ main.py # Streamlit app (fully documented)
βββ alarm.mp3 # Audio alarm played on session complete
βββ screenshot.png # App screenshot
βββ pyproject.toml # Project metadata and dependencies
βββ uv.lock # Locked dependency versions
βββ Dockerfile # Container image
βββ docker-compose.yml # Compose configuration
βββ README.md- Python 3.11 or newer
alarm.mp3in the project root (replace with any MP3 you like)
uv is a fast Python package manager.
# Install uv if you don't have it
curl -Ls https://astral.sh/uv/install.sh | sh
# Run the app
uv run streamlit run main.pypip install streamlit
streamlit run main.pydocker compose up --buildOr without Compose:
docker build -t pomodoro-timer .
docker run -p 8501:8501 pomodoro-timerThen open http://localhost:8501 in your browser.
| Control | Action |
|---|---|
| π Focus / β Short Break / πΏ Long Break | Switch mode and reset timer |
| βΆ Start | Begin counting down |
| βΈ Pause | Pause without losing progress |
| βΊ Reset | Restart the current session from full |
| β Skip | Complete the current session early and advance |
π΄ = completed this cycle
π = currently in progress
βͺ = upcoming
Open βοΈ Settings at the bottom to change durations:
| Setting | Default | Range |
|---|---|---|
| Focus | 25 min | 1 β 90 min |
| Short break | 5 min | 1 β 30 min |
| Long break | 15 min | 1 β 60 min |
| Auto-start next session | On | On / Off |
Click β Apply & reset timer to save.
Drop any MP3 file into the project root and rename it alarm.mp3. The file is base64-encoded at startup and embedded directly in the page β no static file server required.
Rather than subtracting a fixed 1-second interval each tick, the timer measures the actual wall-clock elapsed time between Streamlit reruns:
dt = time.time() - ss.last_tick
ss.remaining = max(0.0, ss.remaining - dt)This means timing is accurate even if a rerun takes 1.3 seconds instead of exactly 1.0. A 25-minute session completes within ~1 second of the true mark.
sessions_in_cycle tracks position within the current set of 4, and is incremented before deciding the next mode:
ss.sessions_in_cycle = (ss.sessions_in_cycle + 1) % LONG_EVERY
next_mode = "Long Break" if ss.sessions_in_cycle == 0 else "Short Break"When sessions_in_cycle wraps back to 0, the user has completed 4 focus sessions and earns a Long Break.
Contributions are welcome! Feel free to open an issue or pull request for:
- Bug fixes
- UI improvements
- New features (keyboard shortcuts, desktop notifications, themes)
- Documentation improvements
Please keep PRs focused and include a clear description of what changed and why.
MIT β see LICENSE for details.
