The engine
A solver, not a calendar.
Most scheduling software takes bookings against slots a human already worked out. This works out the slots. It is a constraint solver — Google's OR-Tools CP-SAT — searching for the arrangement that places the most people without breaking a single rule.
Rules it will never break
Hard constraints are not preferences and they are not weighted. If a schedule would violate one, that schedule does not exist as far as the solver is concerned. There is no slider that turns these off, because a timetable that double-books a room is not a cheaper answer — it is a wrong one.
- →Nobody is in two places at once. An attendee is never placed in overlapping sessions.
- →An event never overlaps itself. If it runs three times a week, those three sessions are distinct.
- →Availability is absolute. A person is only ever placed inside the windows they gave you — and no windows at all means available always, not unavailable.
- →No instructor is ever double-booked, and none is scheduled outside their availability.
- →No room is ever double-booked, and a room is never given an event bigger than it holds.
- →Session capacity is never exceeded.
- →A fixed-time event keeps its clock time. The solver only chooses which day.
- →An equivalence group places each attendee in exactly one of its events — never none, never two.
- →A pinned session stays exactly where you pinned it.
Preferences you actually control
Everything that is a matter of taste rather than physics is a weighted preference, and you set the weights before each run. Six sliders, each 0 to 100. Turn one to zero and the solver stops caring about it entirely.
| Preference | Default | What it does |
|---|---|---|
| Maximise enrolment fulfilment | 100 | Place as many enrolled people as possible. The reason anyone runs this at all, so it starts at the top. |
| Honour high-priority enrolments | 80 | An enrolment marked high priority counts double when the solver has to choose between two people for one seat. |
| Balance session attendance | 60 | Spread people evenly across the sessions of an event rather than filling the first one and leaving the last half empty. |
| Prefer distinct days | 50 | When an event runs several times a week, spread those sessions across different days. |
| Prefer attendees’ starred times | 40 | Attendees can star the windows they would rather be in. Availability is a hard rule; a star is a wish. |
| Minimise instructor back-to-back | 30 | Leave a gap between the sessions one instructor teaches, instead of stacking them nose to tail. |
Why the weights cannot betray you
The objective is strictly tiered. Placing one more person outranks every soft preference added together, and the preferences in turn outrank the tie-breaks. So you can set the weights however you like without ever discovering that the solver dropped somebody from the timetable to win a nicer instructor gap. It cannot. The arithmetic forbids it.
Pin what works. Re-run the rest.
Almost every run is a second run. You look at the week, three sessions are exactly right, the rest is not. Pin the three. The next run treats them as fixed — day, time, instructor and room — and rearranges everything else around them.
Pins carry themselves forward: a run that honours a pin marks its own output pinned too, so you can pin a little, run, pin a little more, run again, and converge without re-pinning from scratch each time.
Small change in, small change out
The awkward truth about optimisers is that they are unstable. Add one attendee, re-run, and you can get a completely different timetable because the new one scores a fraction of a percent better. Mathematically correct; socially disastrous, because forty people already moved their Tuesday.
A run can be anchored to the previous one. Sessions, instructor assignments and room assignments that can stay where they were are rewarded — but at the weakest tier of the objective, below every real preference and far below placing people. Stability is a tie-break, never a trade. The previous answer is also handed to the solver as a starting hint, so it spends its time improving your week rather than rediscovering it.
Four answers, and they mean different things
"It didn't work" is not a useful result. Every run comes back as one of four distinct states, because the right next move is different for each.
- Best possible
- Proven optimal. There is no better arrangement under the constraints and weights you gave it — not "none was found", none exists. Stop looking.
- Stopped at the time limit
- A good schedule, found and kept, plus the optimality gap: an upper bound on how much a longer search could still improve it. A 2% gap tells you to publish; a 30% gap tells you to run it again.
- Ran out of time
- No schedule found yet. This is emphatically not the same as impossible — it usually means the problem is large or tightly constrained. Loosening one window is often enough.
- No schedule possible
- The hard constraints genuinely contradict each other. Something has to give, and the diagnosis below tells you what.
The search budget is derived from the size of the model rather than being a flat timer, so a small project answers quickly and a large one is given the time it actually needs — roughly twenty seconds at the low end, a couple of minutes at the top.
It tells you why, not just no
Every person the solver could not place comes back with a categorised, readable reason rather than a shrug:
- Not available
- Their windows do not overlap any time this event could run.
- Full
- Every session of the event was already at capacity.
- Clashes
- Placing them here would collide with another event they are enrolled in — and it names that event.
- No slot fits
- The event has no candidate time that satisfies its own day, window and duration rules.
- Mixed
- More than one of the above applies across the event’s sessions.
And it stops you before you waste a run
Some problems are visible before the search starts, so they are checked first. An attendee with no availability at all. An event needing more weekly hours than its window contains. More sessions per week than can fit without overlapping. Demand that exceeds total capacity. Instructor and room clashes.
Most of these are warnings you can acknowledge and run through anyway. Two are hard stops — an event that requires an instructor when no eligible instructor is ever free, and the same for rooms — because those cannot produce a schedule and the run would only be a slower way of finding that out. Each one names the event and the fix.
The shapes most tools cannot express
- Attend one of these
- An equivalence group is a set of interchangeable events — three sittings of the same class, four practical groups. Enrol someone in the group and the solver picks whichever member fits their week. Exactly one, guaranteed.
- Attend some of these
- A partial enrolment says "any two of the four sessions". You are not forced to choose between all and nothing.
- All or nothing, when it matters
- And when partial placement is not acceptable, one run flag makes every enrolment all-or-nothing instead.
- Fixed times, free days
- An event that must start at 18:00 but could be any weekday is a first-class case, not a workaround.
- Eligibility, not just availability
- Instructors and rooms carry lists of what they are allowed to take. A room also has to be big enough. Event groups fan that eligibility out across every event in them at once.
Then compare the runs
Drafts accumulate; you choose what to publish. Any two runs can be put side by side, and the difference is reported the way a human would describe it: this session moved (one change, not a deletion and an addition), this person gained a place, this person lost theirs, this instructor now teaches a different week. Attendees and instructors are diffed separately, because they need different conversations.
That same difference drives who gets emailed — only the people whose week actually changed. And the whole loop, in order, is on how it works.
What the engine does not do
- →No manual drag-and-drop override. Every placement comes from a run, which is the only way the result stays provably conflict-free. Pinning and fixed times are how you steer it.
- →Weekly repeating patterns only. There is no multi-week rotation, no dated one-off roster, no exam-timetable mode.
- →No labour rules. Maximum hours, overtime, break law and required coverage per slot are not modelled.
- →No cover or substitution flow yet. Re-running with the absent instructor marked unavailable is the current answer.
See it on your own timetable.
14 days, no card. Everything on this page works on the trial and on every paid plan — the tiers differ only by how many attendees and events you can hold.
Keep reading
- How it worksThe full loop, from an empty project to a published week.
- What you enterEvery field, and which ones are actually required.
- The attendee experienceEnrolment links, permanent personal timetables, calendar feeds.
- Branding and emailsYour logo everywhere, and telling people only when it matters.