SetoffIQ is a free airport pickup and drop-off planner that combines available flight, journey and weather information to help you decide when to set off.
Open SetoffIQ — no account, no API key, no install.
Airport pickups go wrong in a specific, avoidable way: the driver has no idea when the passenger will actually walk out. So they leave early, park, and wait — paying for parking and burning an hour — or they leave on the scheduled arrival time and the passenger stands on a kerb.
The scheduled arrival time is not the useful number. Between touchdown and someone appearing in arrivals there is taxiing, disembarkation, possibly border control, a bag belt and a walk. That gap is routinely 25–60 minutes and it is invisible to the person sitting in the car.
It works back from when the passenger is realistically going to be ready:
landing (live estimate, or the scheduled time)
+ getting off the aircraft, border control, bags, the walk out
= passenger readiness window
− time needed at the airport for the chosen pickup style
− the slowest plausible drive
= leave at this time
Using the slowest end of the drive is the whole trick. It puts the driver's worst case on the passenger's best case, so the driver is rarely early enough to wait around and rarely late enough that the passenger does.
Drop-offs work the same way in reverse, from the departure time.
The recommendation leads with one number, because that is the only thing anyone actually needs:
RECOMMENDED DEPARTURE ●●○ MEDIUM CONFIDENCE
14:11
Today · MAN time Leave at 14:11
That is 2 hr 57 min from now, and puts your
passenger at the terminal by about 15:14.
AT THE TERMINAL ARRIVE AT AIRPORT JOURNEY
15:09–15:14 14:51–14:56 40–45 min
Then, beneath it, what that answer rests on:
WHAT'S AFFECTING YOUR TIMING?
◻ TIME AT THE TERMINAL 120–180 min — a SetoffIQ assumption … Estimated
◼ ROUTE 40–45 min, routed over real roads … Live · 11:14
◻ FLIGHT The departure time on your booking. From you
◼ AIRPORT CONDITIONS Marginal conditions Live · 10:50
◼ ROAD DISRUPTION 13 roadworks nearby, none closing a road Live · 11:12
Confidence is derived from those rows, so the score can never disagree with the table explaining it.
- A deterministic prediction engine. Same inputs, same recommendation, every time. No randomness, no model deciding what time you leave.
- Live aircraft positions where they exist, via a scheduled snapshot from adsb.lol, matched to your flight number by callsign — or picked from a list of aircraft inbound right now, each with its airline and reported origin, or from flights that usually land in the next twelve hours, from SetoffIQ's own record of landings.
- Real routing to the terminal itself over OpenStreetMap data, plus the aerodrome's own weather observation and road closures. A fresh closure or incident changes the journey estimate only when its reported location is on or very close to the driver's route.
- Passenger stages, because a landed flight is not a ready passenger — and the stages that are inferred rather than observed say so.
- Windows, not false precision. "Ready between 18:55 and 19:15", never "ready at 19:03".
- Every number is sourced. Each factor is tagged as live data, something you entered, or a SetoffIQ assumption. They are never presented as one another.
- Monitoring that recalculates when the flight moves, tells you what changed and why, and keeps an auditable history.
- Graceful degradation. Weather down? Recommendation still works, without a weather allowance. Routing down? Estimated from distance, and it says so.
- Local-first. No account, no server, no tracking. Saved journeys live in your browser.
- £0 to run and £0 to use, by design rather than by luck.
You
│
SetoffIQ UI (React, static)
│
┌────────────┼────────────┬──────────────┐
│ │ │ │
Flight Weather Routing Postcode
snapshot Open-Meteo OSRM postcodes.io
(adsb.lol via
GitHub Actions)
│ │ │ │
└────────────┴─────┬──────┴──────────────┘
│
Provider adapters
(timeouts, retries, caching, dedup,
request counting, stale handling)
│
Normalised domain objects
│
Prediction engine
(readiness window → arrival window →
departure time → confidence)
│
Recommendation
│
Explanation (rule-based, or a local
model that only rewords the result)
│
You
The important line in that diagram is the one below the prediction engine. The engine decides every time and every window. The explanation layer only puts it into words — it cannot change a single number. That keeps the product deterministic, testable and debuggable, which a language model in the decision path would not be.
This is the part most projects quietly fake, so to be explicit about what is and is not possible for free:
adsb.lol gives aircraft positions — where something broadcasting a given callsign is, how fast, how high, which way it is heading, climbing or descending. The Virtual Radar Server standing data adds the route reported for that callsign. Neither gives airline schedules, gate information, or any status for a flight that has not taken off.
A scheduled GitHub Actions job fetches both about four times an hour and publishes one static JSON file alongside the app, so no visitor's browser calls a flight-data service and usage does not grow with traffic. adsb.lol publishes its data under the Open Database Licence, which permits exactly that; the routes are public domain (CC0).
The app used The OpenSky Network until 11 September 2026, when its terms turned out to require a written agreement for any operational use of its API — see DATA-SOURCES.md.
So SetoffIQ can tell you "that aircraft is 60 km out, descending, so expect it on stand about 18:34". It cannot tell you "your flight is delayed" before the aircraft is in the air, and it does not claim to. When no aircraft is found, it uses the scheduled time you entered, unchanged, and says exactly that on screen.
Not every airline helps, either. Ryanair and easyJet broadcast alphanumeric
callsigns unrelated to the flight number on your ticket, so those will not match
a flight number you type, and some airlines vary their callsigns — the standing
data lists Emirates' Dubai–Manchester service as both UAE21 and UAE1KM. The
app is honest about that rather than guessing.
Everything that matters stays on your device. Saved journeys, your starting postcode, flight numbers, settings and caches all live in this browser's local storage. There is no account and no server holding any of it.
What does leave your device: your postcode goes once to postcodes.io to become coordinates; your coordinates and the airport's go to a public OSRM instance for the drive; the airport's coordinates go to Open-Meteo for the weather. The explanation layer never receives your address, postcode or coordinates — the drive reaches it only as a duration.
There is no analytics, no advertising and no third-party tracker of any kind.
Settings has a Clear all local data button that removes saved journeys, preferences, provider caches and downloaded voice model/style files. Journey data is managed by one namespaced storage module; voice assets use isolated caches. Cached app-shell and speech-runtime files may remain, without personal data.
Owner: £0. User: £0. Not "free tier, watch your usage" — there is no billing mechanism attached to anything in the stack, so a mistake cannot produce a bill.
See COSTS.md for the audit and DATA-SOURCES.md for each provider's current terms and the date they were checked.
| Provider | Provides | Terms |
|---|---|---|
| adsb.lol | Aircraft positions | Free, keyless, ODbL 1.0 — republishing with attribution permitted |
| Virtual Radar Server standing data | Reported routes by callsign | CC0 1.0 (public domain), community-submitted |
| AirLabs | Manchester's schedule, delays and cancellations | Free plan, keyed (CI only), 1,000 requests/month; refreshed every 4.5 hours |
| NOAA Aviation Weather Center | Aerodrome observations (METAR) | Public domain, no key, no billing |
| National Highways | Road and lane closures | Free, keyed, OGL 2.0, redistribution permitted |
| TomTom Traffic Incidents | Optional accidents, jams and roadworks | Keyed; one shared request every 20 minutes when enabled |
| Open-Meteo | Weather | No key, 10,000 calls/day, non-commercial, CC BY 4.0 |
| OSRM (FOSSGIS + project instances) | Driving time and distance | No key, no billing, no live traffic |
| postcodes.io | UK postcode → coordinates | MIT, ONS/OS open data, no key |
| OpenStreetMap | Road network | ODbL, attributed in the app |
| Ollama (optional) | Explanation wording | Runs on your own machine; off by default |
Stated plainly, because a planner that oversells itself is worse than useless:
- No airline schedules. SetoffIQ cannot know a flight is delayed before the aircraft is airborne. You supply the scheduled time from your booking.
- Callsign matching is partial. Ryanair and easyJet flight numbers do not correspond to broadcast callsigns.
- No live traffic. OSRM models free-flow driving. The upper bound of every journey estimate absorbs that, but it is an allowance, not a measurement.
- No queue data. Border control, security and baggage times are SetoffIQ assumptions, documented in DATA-SOURCES.md and labelled as assumptions in the app. They are not airport statistics.
- National Highways disruption covers major roads only. National Highways operates the Strategic Road Network, so the M56 and M60 are covered and local roads are not. The feed reports planned works and unplanned closures, not every accident or traffic jam. Route matching uses event locations, not measured traffic delay. Old snapshots and events merely near the airport do not widen the estimate. Routine roadworks are reported but deliberately do not widen the estimate — dozens of live lane closures is the normal state of the network, and a warning that is always on is not a warning.
- No flight schedules. Nothing free will tell you which flights land tomorrow, so the inbound picker can only offer aircraft already in the air.
- No parking prices. There is no reliable free source, so none are shown.
- Monitoring only runs while the app is open. A browser app cannot run in the background when the browser is closed, and this one does not pretend to. It checks while open, and refreshes immediately when reopened.
- The position snapshot can be up to ~15 minutes old, and GitHub's scheduled runs are best-effort. Every card shows its own age, and staleness lowers confidence.
- Manchester only, for now. Airports are data, not code — adding one is a profile, not a rewrite.
git clone https://github.com/sayamdev/setoffiq.git
cd setoffiq
npm install
npm run devNo configuration is needed. There are no API keys anywhere in this project.
To refresh the flight position snapshot locally:
node scripts/fetch-flight-snapshot.mjsOptional: to have a local model word the explanations, run Ollama
(ollama serve, ollama pull llama3.2) and switch it on in Settings. The
published site never uses it.
npm test # unit, integration and end-to-end tests
npm run typecheck # strict TypeScript
npm run lint # oxlint
npm run build # production buildPushing to main builds and deploys to GitHub Pages via
.github/workflows/deploy.yml. The same
workflow runs on a schedule to refresh the flight snapshot, so the published
site stays current without anyone committing data.
To deploy your own copy: fork, enable Pages with Source: GitHub Actions in repository settings, and push. Nothing else is required — no secrets, no environment variables, no billing account.
MIT.
SetoffIQ is an independent travel-planning application and is not affiliated with any airline or airport. It provides estimates for planning purposes. Flight times, passenger processing, traffic and weather may change — allow appropriate additional time and follow official airport and airline guidance.
Open Plan a pickup or Plan a drop-off, then tap Start talking in Talk through your journey. Microphone use starts only after that tap and browser permission. For example:
“I'm picking someone up at 6:30 today, Ryanair from Rabat.”
SetoffIQ shows a live transcript and the structured details it understood. If 6:30 could mean morning or evening, it asks AM/PM; if the time could be a meeting time, it asks for the flight landing time. Say “PM” and “landing time”, or tap the answers. Enter the starting postcode once in the understood-details controls. With enough information, the existing deterministic engine runs automatically. Its set-off time, windows, confidence and data caveats appear on screen and are read aloud when a local English voice is available.
- Correct a detail: “no, 7 o'clock”, “actually 19:00”, “tomorrow”, “hand luggage only”, “terminal two”, or open Understood details and edit a field.
- Changing the flight or its origin clears route/terminal details inferred from the old flight, while retaining choices you explicitly entered. Unanswered time questions remain pending when you add another detail such as a postcode.
- Ask “why that time?” for the standard explanation, or “start monitoring” to save the structured journey and open the existing monitoring screen.
- Multiple matches are offered as numbered options. Say “option one” or tap one. Matching uses the existing schedule, timetable and inbound-position adapters, including their age limits. A timetable match carries no live status.
- No match means the stated time is used; it does not prove cancellation. Ryanair/easyJet callsign limitations still apply. No fuzzy place or flight guessing is used: inspect a mishearing and correct it.
- “About half six” is accepted and visibly marked approximate, then treated as the entered flight time. It does not create a fabricated uncertainty window.
- Replies pause recognition to prevent the app hearing itself, then listening resumes. Tap Interrupt and listen to cut a reply short. Stop voice, “stop listening”, or Escape stops voice. After a minute without an instruction, voice pauses. There is no wake word or always-on microphone.
- Use Or type an instruction in any browser; it runs the same parser and action validation. The original journey form is still available.
Recognition is feature-detected (SpeechRecognition / webkitSpeechRecognition),
with English (UK) selected. Chrome and Safari commonly expose this API, but support
varies across operating systems, installed PWAs, permissions and browser settings;
Firefox generally needs typed input. HTTPS (or localhost), microphone permission
and often an internet connection are required. Support is not a guarantee of
latency or accuracy, especially with cabin noise. See the
Web Speech API documentation.
Your browser may send microphone audio off-device to its speech service. That service’s handling and retention follow its provider’s terms; SetoffIQ cannot control them. This is disclosed above Start talking before microphone use. Type your postcode or other sensitive details if you prefer to keep them out of speech. SetoffIQ does not record, log or persist audio or transcripts, and sends no transcript, postcode or coordinates to any language model. Voice parsing is local, rule-based and independent of the optional Ollama explanation setting. Spoken device replies use only voices the browser marks local; if none are available, the answer remains on screen. Read answer can retry after voices load or when autoplay restrictions require a tap. Only normal structured journey details are persisted when monitoring is started, through the existing storage module.
Voice stops on tab hiding, page exit and screen lock when the browser dispatches those lifecycle events; returning requires a fresh Start talking tap. Browser suspension can interrupt recognition without a final transcript. Monitoring retains its existing limitation: it works while the app is open, with a refresh on return. No background voice or locked-screen reliability is promised.
- The bounded English parser is not general conversation. Today/tomorrow/ISO dates, clock times, half/quarter times and common airlines work; unsupported date wording or unclear instructions need the controls. More languages and place aliases would need additional deterministic rules and test corpora.
- Browser recognition owns transcription speed and accuracy. Truly private, consistent offline dictation would require an on-device speech model and large downloads, with device-performance testing. No hosted transcription is added.
- Spoken barge-in during a reply is not enabled: interrupt with a tap, then speak. Reliable simultaneous listening/speaking would need tested echo cancellation across actual mobile devices. Replies and follow-ups otherwise need no new tap.
- Flight data remains incomplete, region-limited and refreshed best-effort. Better coverage would need a permitted, sustainably free feed or an explicit change to the project’s cost constraint. Voice cannot supply missing airline information.
- Real phone microphone, noisy-car, screen-reader speech and OS lock behaviour still require device testing; CI covers utterance strings and browser callbacks, not physical audio capture. Use the app while stationary.
Repository documentation correction: this checkout already supports optional AirLabs schedule/timetable snapshots and CI-only provider keys (see DATA-SOURCES.md and ARCHITECTURE.md). Older “no schedules” / “no keys anywhere” statements above describe the earlier position-only version. The voice feature adds no backend, accounts, credentials or metered services, and preserves the £0-to-run / £0-to-use design.
SetoffIQ prepares a British Kokoro voice (Emma) automatically. Voice settings on the planning page offers a manual download or retry. It prepares automatically after the first page renders. A saved device-voice choice is respected; offline, Data Saver and reported slow connections defer the download. Expect roughly 115 MB of model/style files on CPU, or roughly 350 MB on supported GPUs, plus a ~22 MB speech runtime; the first download and warm-up may take a few minutes. Try natural voice previews it without enabling the microphone. Use device voice cancels loading/playback; Remove voice download deletes the isolated model/style cache. Settings → Clear all local data also removes these files. Browser-cached app/runtime assets may remain.
The choice is remembered through the normal SetoffIQ storage layer. Reopening a planning page recreates the worker from cached model files, when the browser has kept them. If files were evicted it downloads again. Until ready, replies use a local browser voice, preferring British and enhanced voices. The UI identifies which voice is actually speaking. Neither download nor synthesis blocks the on-screen result. If the model is too slow, fails, or playback is blocked, the app falls back to a local device voice and keeps the written answer.
Replies lead with the set-off time once, use spoken clock/date/range formatting, and distinguish passenger readiness from terminal arrival. Late journeys retain the engine's warning; low confidence is not spoken as certainty. “Why that time?” explains the engine's exact journey range and airport allowances in plain language. No model interprets the journey or rewrites any numbers. Kokoro converts that already-determined text to audio only. Long replies are split into short sentences so the model cannot silently truncate the end. Stop, Escape, corrections and tab hiding cancel playback and discard obsolete audio; no generated audio is saved.
Privacy: Hugging Face receives your IP and requests for public model/style files. Speech generation happens inside a browser worker: no reply text, postcode, coordinates or microphone audio is uploaded to Kokoro or a hosted voice API. Browser recognition may still send microphone audio to its own speech service, as disclosed above. Model weights are cached separately from saved journeys and cleared by the same Settings action. Cached weights are software assets, not recordings. Kokoro/Transformers.js and the model use Apache-2.0 licences; see THIRD-PARTY-NOTICES.md.
Natural speech requires workers and WebAssembly, and enough memory for the model. Chrome desktop download/playback/reload are checked; actual iOS/Android audio policies, memory limits, noisy-car recognition and subjective voice quality need physical-device/user testing. Small gaps can occur between spoken sentences on slower devices. VoiceStudio's local API was considered, but requiring visitors to install/run another app conflicts with the no-setup public phone experience.
The homepage leads to Plan with voice. On a planning page, Voice or text and Use the form show one input method at a time; switching to the form stops the microphone. The original form retains its fields and behaviour. Results keep the recommendation visible; the supporting timeline and source details are under Why this time? and Travel notes and data limits.
The conversation asks for the next missing detail, including AM/PM or flight-time clarification when needed. The microphone control changes from Start talking to Listening — stop, Checking your journey, or Interrupt and talk. Tap a value in Your journey details to correct it; applying a change runs the same validated planning pipeline. Edit journey, Explain this time, and Start monitoring sit together beneath the result. Extended instructions live under Voice help and privacy; the microphone privacy disclosure stays visible. Switching a completed plan to Use the form fills it with the current journey; submitting returns to the result stage. The form's original validation is retained.
Routine spoken replies are shorter and help follows the current step. The first result reads the engine's departure time, confidence and flight-data limitations; the full windows remain on screen. Kokoro is synthesised speech, not a human recording. No human-recorded prompt assets are included; adding them later would require original or appropriately licensed recordings. Dynamic answers continue to use local synthesis.
A short first-visit guide can be skipped and replayed from How to use SetoffIQ in the footer. Its correction example runs through the real local parser without creating a journey or saving the example. A small, lazy-loaded Lottie transition honours reduced motion. Only the guide-dismissal preference is saved.
Voice now also understands help, repeat that, edit details, start again, open journeys, open settings, and go to home. Navigation ends the voice session; return to the planner and tap Start talking to resume. Multiple flight choices are read aloud. Missing postcodes have a focused inline field, so sensitive location information need not be spoken to the browser service.
Natural speech prepares automatically after the first render, without requesting microphone access or autoplaying sound. The visible preparation notice and welcome guide let visitors cancel and remember their choice. Automatic preparation is skipped for a saved opt-out, offline mode, Data Saver, or a reported 2G/3G connection. Browsers do not always expose connection type: an unreported mobile connection may still download the model. Device speech and all text controls work while it loads.