ElhamDevelopmentStudio/ballpark

A focused planning tool for tech leads, EMs, and founders sizing a roadmap.

★ 1Forks 0TypeScriptGitHub ↗Compare

README

Ballpark

Replace false-precision deadlines with real probability ranges.

Ballpark is a focused planning tool for tech leads, EMs, and founders sizing a roadmap. You add tasks with three‑point estimates (best / likely / worst hours), tag the risky ones, and Ballpark runs a 10,000-trial Monte Carlo simulation to show you the dates you can actually defend.

It's local-first: no account, no backend, nothing leaves your browser tab.

Why this exists

Most teams give single-date deadlines that are either hopelessly optimistic or padded with hidden buffer. The honest answer to "when will this ship?" is a probability distribution, not a date. PERT/Monte Carlo techniques solve this — but the existing tools are either spreadsheet macros or enterprise project software.

Ballpark is the small, focused tool in between.

Features

  • Three-point estimation table with risk tags (novel-tech, external-dep, unclear-spec, integration, design-heavy) that widen the spread on a per-task basis.
  • Timeline view — same data laid out as a sequential pipeline on a date axis, with uncertainty whiskers per task and project-level P50 / P75 / P90 markers.
  • Drag-and-drop reordering in both views, plus keyboard arrows for accessibility.
  • 10,000-run Monte Carlo with a deterministic seed, so results are stable while you edit.
  • Headline forecast — three percentile cards with opinionated copy ("the date you can defend to leadership" for P90).
  • Distribution chart with hover tooltips mapping each bar to a calendar date and run percentage.
  • Sensitivity panel ranking tasks by their share of total variance — directly shows where to cut to tighten the forecast.
  • Multi-project sidebar with create / duplicate / delete / JSON export.
  • Three realistic seeded sample projects on first load so the tool is useful in 10 seconds.
  • Mobile-responsive layout.

Run it

npm install
npm run dev      # http://localhost:5173
npm run build    # production build into dist/
npm run preview  # serve the production build

How the math works

Each task samples from a triangular distribution defined by your three estimates (a, c, b) = (best, likely, worst). Sampling uses the inverse CDF — exact, fast, no rejection sampling.

Risk tags scale the spread without moving the mode: each tag adds 12% to the optimistic↔pessimistic distance (capped at 1.6×). So a task tagged novel-tech, external-dep has the same most-likely value but a meaningfully wider uncertainty range.

For each of 10,000 trials we sample every task, sum, and apply your contingency multiplier. The sorted totals give us the percentiles. Sensitivity is the closed-form variance of each task's triangular as a share of total variance (assuming task independence — fine for this use case).

A deterministic PRNG (Mulberry32) seeded from a hash of the project's task data means the histogram doesn't flicker as you edit unrelated fields.

Calendar model

Hours-of-work convert to calendar days via the project's Capacity (hrs/week) setting. We don't model working-days vs. weekends — most projects burn at a roughly constant rate per calendar week regardless of which days are which, and pretending we know your team's holiday schedule is false precision.

Tech stack

  • Vite + React 18 + TypeScript (strict, with noUnusedLocals / noUnusedParameters)
  • TailwindCSS
  • Hand-rolled SVG charts (no chart library)
  • Inline icon set (no icon library)
  • localStorage persistence

Total bundle: ~187 KB JS / ~27 KB CSS.

Project structure

src/
├── App.tsx                       # top-level layout, project CRUD
├── types.ts                      # Project, Task, RiskTag, SimulationResult
├── lib/
│   ├── simulate.ts               # Monte Carlo, triangular sampler, date math
│   ├── format.ts                 # hour/date formatting
│   ├── storage.ts                # localStorage wrapper
│   └── samples.ts                # seeded sample projects
├── hooks/
│   └── useReorder.ts             # HTML5 drag-and-drop reorder hook
└── components/
    ├── Sidebar.tsx               # project list (desktop)
    ├── MobileNav.tsx             # project switcher (mobile)
    ├── ProjectMeta.tsx           # name/dates/capacity/contingency
    ├── HeadlineForecast.tsx      # P50/P75/P90 cards
    ├── TasksSection.tsx          # wrapper with view toggle
    ├── TaskTable.tsx             # table view of tasks
    ├── TimelineView.tsx          # pipeline / Gantt-style view
    ├── DetailedForecast.tsx      # distribution chart + sensitivity
    ├── DistributionChart.tsx     # SVG histogram
    ├── Sensitivity.tsx           # variance-share ranking
    ├── GuideModal.tsx            # "How Ballpark works"
    ├── Welcome.tsx               # first-run / no-project state
    └── Icons.tsx                 # inline SVG icon set

Storage

All data lives in localStorage under three keys:

  • ballpark.projects.v1 — array of Project
  • ballpark.activeProjectId.v1 — currently selected project id
  • ballpark.taskView.v1 — last-used view mode (table or timeline)
  • ballpark.seeded.v1 — first-run flag so samples only seed once

Use Export in the project header to dump a project to JSON.

What this is not

  • A project tracker — there are no statuses, assignees, or due dates per task.
  • An estimation game (planning poker, t-shirts) — those are upstream of this tool.
  • Multi-user — everything is local.

If your team needs any of those, Ballpark hands you off cleanly: the JSON export is a clean record of "here's what we estimated and why."

Contributors

ElhamDevelopmentStudio

Issues