Calendar Lab
Fun with calendars. Who is on what, what overlaps, and what is left.
How it works
How it counts
- Capacity: Each resource contributes its weekly hours, spread evenly across the working days you picked. A partial week at the edge of the range only counts the days it actually contains.
- Weekends stay on screen as shaded columns but carry nothing, and a bar crossing one is washed out over those days. A weekend release that really costs people is claimed per event. You can pick the exact Saturdays and Sundays in the inspector, and each adds that person's day of capacity and spends it.
- Public holidays behave like a weekend that only some people keep. Pick a country in Setup and the days are fetched once, each with the regions it applies to; a plan can then say where every person sits. Bavaria keeps Heilige Drei Könige, Fronleichnam and Allerheiligen, Berlin keeps Internationaler Frauentag, so a team split across offices counts differently in the same week. A day the whole team keeps closes the column outright; a day only some of them keep stays open, marked with a rule along the top, and comes off just their capacity — no phantom clash for the people still working. An event can claim a holiday back the same way it claims a Saturday.
- Company days are the ones no public list knows about: the shutdown between Christmas and New Year, or the half day on New Year’s Eve. Add them in Setup with how much of the day goes — closed, half, or a quarter — and they come off everybody at once, so a person added later is covered without touching anything. Every year repeats one on the same date, and a day can be limited to some people when only one office closes. A half day shows a rule across half the column, and the work booked that day simply shrinks to fit: nothing turns red for it.
- Time off removes capacity for the people it is assigned to, at the percentage you set (i.e., 100% for a full absence, 50% for half days).
- Work consumes load × hours-per-day for every assigned person, every working day it spans. Two events at 50% on the same person fit; two at 80% do not, and the days at fault are marked red.
- Free time per calendar week is available minus booked on every row in the week view, under each week in the timeline's load strip, and per person on hover.
Two views
- Weeks: One calendar week per line, that week's working days across the full width, its booked and free hours on the right. An event crossing a week boundary is cut into one segment per week, marked ‹ and› where it continues; drag a segment onto another row to move the whole event to that week.
- Timeline: One row per event, per resource or per colour, with every day as a column. Best for seeing how things line up against each other over a long range.
Handling
- Drag a bar to move it, drag its edges to stretch it; hold⇧ to snap to week boundaries. Nothing re-packs until you let go.
- Drag across empty days to make an event spanning them: press, sweep, let go, and it is created and selected ready to name. Double-click makes a single-day event on the day you clicked. On the timeline either way inherits the row you drew on.
- N new · V switch view · ←→ move a day (⌥ stretches, ⇧ jumps a week) · ⌫ delete ·⌘Z undo.
- On a phone the panel is a sheet at the bottom — tap a tab or an event to raise it, and the chevron to raise or drop it. Tap an event to select it and drag it to move. To add one, press and hold a day and then drag across the days it covers; a plain swipe still scrolls the calendar, and a double-tap makes a single day.
Getting it out
- Your plan NEVER leaves the browser. It lives in local storage. Export it as one image, as A4 pages for printing, as JSON, or as a link that carries the whole plan through base64 encoding (remember, the plan never leaves the browser). The single exception is the public holiday list: choosing a country in Setup fetches its dates from
date.nager.at. Nothing about your plan is sent with that request, and the answer is cached and then stored in the plan itself, so exports and share links work offline afterwards. - The image redraws whichever view you have open, scaled to fill the sheet when printing. A title that cannot fit on its bar is shortened and listed underneath, so the picture never loses one.
- Both formats come in portrait or landscape: A4 turns the sheet and re-flows the pages; an image widens or tightens its day columns to match, and the panel shows the pixel size you will get.
Writing a plan instead of drawing one
- Paste JSON into the Export tab, or open a link ending in
#p=<base64 of the same JSON>— the whole plan travels in the URL, so anything that can write text can hand you a working calendar. - Names stand in for ids and anything new is created, calendar weeks stand in for dates, and fields answer to their obvious synonyms (
who,category,type,days). What was understood is reported back as it loads. "holidays": "DE-BY"is enough to pull a country's public holidays in; the days are fetched when the plan opens. Give a person their own"region"when they sit somewhere else, and list your own closures under"companyDays".- The whole format is described at/projects/calendar-lab/schema.json— one URL to hand to whatever is writing the plan.
{
"title": "Q4 rollout",
"year": 2026, "cwFrom": 41, "cwTo": 45,
"holidays": "DE-BY",
"companyDays": [
{ "date": "2026-12-31", "name": "New Year's Eve", "half": true, "everyYear": true }
],
"resources": ["Ada", { "name": "Bruno", "hours": 32, "region": "DE-BE" }],
"categories": [{ "name": "Delivery", "colour": "Blue" }, "Research"],
"events": [
{ "title": "Kickoff", "type": "milestone", "cw": 41, "from": "Mon" },
{ "title": "Discovery", "cw": 41, "from": "Tue", "to": "Fri",
"who": ["Ada"], "category": "Research" },
{ "title": "Build", "cw": 42, "toCw": 43, "who": ["Ada", "Bruno"], "load": 80 },
{ "title": "Release", "cw": 44, "from": "Sat", "to": "Sun", "who": "Bruno",
"alsoWorking": ["cw 44 Sat", "cw 44 Sun"] }
]
}Loading the planner…
Loading the planner…