Journey
Overhead Flights
0%
Case Study · Independent Project

Overhead Flights

Overhead Flights: real flight data, quietly displayed on a macOS screen saver — showing the one plane passing overhead right now.

MacBook on a desk at night showing Overhead Flights screensaver with callsign BAW118; city skyline and distant aircraft through floor-to-ceiling windows.
Role UI/UX & Product Designer
Focus Research · AI workflow · Integration
Platform macOS Screen Saver
Year 2026
Situation

Mainstream apps all require you to actively open and search; the passive, single-device direction has already been done in hardware.

Market landscape

Most people spend the majority of their day glued to a phone or a laptop. This product starts from that observation: it connects a screen saver with real flight information, using the lowest possible amount of disruption to let someone feel a faint sense of connection to another person.

There's no shortage of plane-tracking tools — FlightRadar24, Plane Finder, FlightAware all lay the whole sky out as a map, a list, an alert feed. They require you to actively open the app to check; at their core, they're dashboards. The maker community has also built hardware for this: Airframe (a Raspberry Pi + e-paper display) quietly shows the nearest plane overhead — a gift its creator built for a friend who lived under a flight path. The idea itself is not something I thought of first.

Type Example Behavior Limitation
Mainstream apps FlightRadar24, etc. Active search, multi-plane map Requires opening, high information density
Maker hardware Airframe, etc. Passive, single device Requires buying parts, assembly, takes up physical space
Ambient screen savers Fliqlo, Aerial Passive, atmospheric No flight information, or unrelated to flight
Positioning

The gap isn't the idea of "a plane overhead" — it's the intersection: a macOS screen saver.

Two axes

After checking, the actual gap isn't the concept of "a plane overhead" — it's the intersection: a macOS screen saver × real flight data × editorial typography. Mainstream apps' business models don't encourage a passive screen saver; Airframe proves the passive, single-device direction works, but it adds a physical device. There's no direct precedent for a software screen saver doing this — the argument has to address Airframe head-on, not pretend the market is empty.

Ambient experience Functional tool Active search Passive appearance
★ Overhead Flights macOS screen saver
Airframe Hardware device
FlightRadar, etc. Active tools
Fliqlo / Aerial Pure ambience
Task

Take the real-time data that used to live only inside tracking apps, and move it into the screen saver that was already idle.

Goal

Move real-time data — the plane currently flying overhead, information that used to live only inside tracking apps — into the screen saver a computer is already running. No new app, no new device, no active searching: just replace what the screen saver shows, so a person can feel a faint human connection while being almost entirely undisturbed.

“I decided to build a screensaver that quietly shows flight information, so people feel connected without any effort.”

Role: independently conceived and decided on direction, visuals, trade-offs, and risk. AI collaborated on research synthesis, drafting, and implementation — rules described below.

Action · AI

Four tools, each with its own job — I'm the gatekeeper at every step.

Collaboration

This wasn't throwing one prompt at a tool and waiting for a result. Four tools, each with a distinct job, and I'm the gatekeeper at every step:

Tool Responsibility
Claude First-pass thinking: breaking down the problem, synthesizing market research, drafting the spec, early technical trade-offs
Perplexity Verifying claims: finding primary documents and research to check whether claims in the market research and design rationale actually hold up
Grok Only catches errors: doesn't redo the thinking; reviews facts, assumptions, risks
Cursor Only executes task packages that have already passed review; doesn't improvise before review

The reason for choosing Perplexity is straightforward: general-purpose AI answers can occasionally sound authoritative while having no real source behind them. Perplexity attaches sources to its answers, which lets me go back and verify instead of taking it at face value. That matters for a portfolio argument that will actually be scrutinized — claims about "where the gap is" in the market research, and the research cited in the design rationale, both need to trace back to real documents, not just "the AI said so."

Gatekeeping examples (this isn't a process demo — it's the judgment itself)

  1. 01

    Perplexity's MCP tool kept returning 404 errors. I didn't wait for the tool to get fixed, and I didn't fill the gap with an AI's unverified memory — I opened a browser myself and manually verified the research instead. The judgment call: when a tool fails, there's a choice between "get something down now, verify later" and "just do it manually." I chose the latter, because this market research ultimately has to support the claim that "this gap is real" — it can't afford to rest on unverified information.

  2. 02

    A draft wanted to state adsbdb's rate limit as a specific number. That number had never been verified against a primary source — it was just a plausible-sounding guess from the AI. I required that "no number goes in without primary-source verification," and changed it to conservative, verifiable language instead (phrasing like "well below the public 1 req/s limit" — checkable, but not overstated). The judgment call: a portfolio gets scrutinized, and an unsourced number is worse for credibility than no number at all.

  3. 03

    The AI surfaced an unfavorable precedent: Airframe. The safer move would have been to avoid mentioning it and let readers assume the idea was original. I chose to address it directly instead: acknowledging that Airframe had already done the "nearest plane overhead" concept earlier, and narrowing the difference down to "form factor (screen saver vs. hardware) + execution (editorial typography vs. e-paper)." The judgment call: not finding a competitor doesn't mean you've won. Being willing to face the competitor you did find head-on is what actually demonstrates product judgment — not how thorough the market research was.

  4. 04

    Once the visual design was finalized, I marked it frozen in the documentation. I told the AI explicitly: "the text size and layout are already right — don't touch them unless I say so" — and confined its remaining freedom to the data layer and background effects. The judgment call: AI has no built-in sense of "good enough, stop here" — left alone, it tends to keep micro-adjusting past the point of visible difference. Freezing the design was a line I drew deliberately; it wasn't something the AI converged on by itself.

Action · Research & Design

First verify whether the market gap is real, then decide which frameworks actually support "quiet."

Research method
  1. 01

    Define the problem itself first, before rushing to research tools. Is the actual gap the feature of "tracking planes," or the experience of "being able to passively notice something without actively checking"? Get the problem definition wrong, and no amount of competitor research afterward will fix it.

  2. 02

    Scan mainstream apps' product logic and delivery format (FlightRadar24, Plane Finder, FlightAware), and confirm their core assumption is "the user will actively open the app to check." That assumption itself is the actual limitation in the market structure — not just a descriptive word chosen at random.

  3. 03

    Go further and check maker projects, articles, and GitHub (Airframe, FlightTracker, etc.), deliberately looking for precedents unfavorable to my own claim, to avoid convincing myself the market was empty. This step exists so the positioning statement in the Situation section can survive the most direct rebuttal: "hasn't someone already done this?"

  4. 04

    Verify API terms against primary documentation (airplanes.live's rate limit, terms of use). Any number without a primary source doesn't go into a public-facing document — better to use conservative, verifiable language than a number that looks more precise but has no traceable source.

  5. 05

    Converge on a single positioning statement that can withstand challenge: acknowledge the precedent exists, and compress the actual difference down to two points — "the screen saver as the medium" + "editorial typography as the execution" — rather than vaguely claiming "nothing like this exists."

Design

Three frameworks, one line each, then down to the actual numbers:

  • Calm technology (Weiser & Brown, 1995): Information stays at the periphery of attention, and only moves to the center when needed. The original paper's motivation was countering the anxiety caused by information overload — not just saving attention.
  • Soft fascination (attention restoration, Kaplan & Kaplan, 1989): Effortless, gentle stimulation (like watching clouds) lets attention rest after a long stretch of focus. That's the reason this product lives in the "idle / lock screen" moment — it's meant to serve a moment of rest, not add one more information source to process.
  • Ambient intimacy / social snacking (Reichelt, 2007; Gardner, Pickett & Knowles, 2005): Passive exposure to a symbol of another person's existence — even without any interaction — can lower feelings of isolation. A stranger's flight quietly appearing is a small signal that "someone else out there is moving through their life right now," almost like someone quietly checking in on you.

How these three frameworks became actual visual decisions:

  • Arousal is driven by brightness and saturation, not hue (Valdez & Mehrabian, 1994) — this is the basis for the near-black background (rgb(0.02, 0.02, 0.025)) plus low-saturation silver-white text. There's no need to lean on the cliché of "cool colors" to create a sense of calm — lowering brightness and saturation directly is enough.
  • The text hierarchy decays by proportional opacity, not equal steps: callsign 0.82 → airline 0.48 → meta 0.34 → monogram 0.28. This proportional decay matches the eye's non-linear perception of brightness differences (Weber-Fechner) — in a low-contrast environment, proportional steps make each level of hierarchy feel evenly spaced, instead of forcing hierarchy through harsh contrast.
  • No cards, no borders, no icons — just a minimal stack of text (aesthetic-usability effect, Kurosu & Kashimura, 1995): people read visual restraint directly as "this thing won't bother me." For a product that appears during lock screen / idle moments, that's a hard requirement — the context of use itself demands "don't disturb."
  • Motion only serves genuine information changes: a new flight fades in over ≤0.4 seconds; an update to the same flight has no animation at all; a slow, multi-minute 4–6px anti-burn-in drift; and it respects the system's "reduce motion" setting. In a low-arousal environment, any unnecessary motion reads as disruption — so it only moves when there's actually something new to show.

Hard constraint of the screen saver: any mouse movement or key press exits it immediately — there's no intermediate state like "click to reveal more." So disclosure is reduced to two honest states:

State Behavior
No plane Near-total black
Plane present Shows callsign, airline, altitude, bearing. As long as it stays within range, it keeps displaying (updated roughly every 90 seconds); only leaving range or losing the flight returns it to black.

The interest comes from the rarity of the event itself (a real plane actually passing over), not from stacking interactions or animation to please. When data can't be fetched, it fails silently into black rather than showing an error — a quiet way to fail, and part of the same "don't disturb" principle.

Action · Data & Build

After three separate WebView failures, the whole architecture moved to native.

Data authenticity
  • Source: airplanes.live (live position) + adsbdb (airline / route information).
  • Radius 40 km, an altitude threshold, and a 90-second polling interval — these three numbers are the result of a trade-off: too small a radius means long stretches of black (which feels broken); too large a radius shows planes that clearly aren't "overhead," undermining the product's core claim. The 90-second polling interval sits well below airplanes.live's public 1 req/s limit, trading a generous safety margin for stability — not having to worry about being rate-limited — at the cost of the data not being second-by-second real-time. But this product wants "a quiet environmental fact," not a tracking dashboard; second-level real-time was never the goal.
  • Without GPS, it falls back to a fixed location (Wembley Park, London), documented as something the user can change themselves — not a hardcoded, unchangeable assumption.
  • Multiple real callsigns verified against the API — this is the bottom line of the entire "plane overhead" claim: if the flight data were fake, the whole point of the project would break. What this product is meant to make someone feel is "a real stranger is genuinely flying overhead right now" — that connection has to be real, or it's just decoration. A portfolio mockup can look beautiful, but faking the data would make the claim itself collapse.
  • Classification: non-commercial, portfolio use only; any rate-limit number without primary-source verification is stated conservatively rather than as a hard figure.

“If the flight data were fake, the whole point of the project would break — the connection has to be real, or it's just decoration.”

Technical integration
Location (or fallback) → airplanes.live (nearest plane, 90s polling) → (optional) adsbdb airline/route → macOS .saver URLSession for data · AppKit for text · Core Image for background → System Settings → Customize screen saver

The original approach handled everything through WebView (rendering, text, data requests), and it hit repeated failures inside the legacyScreenSaver environment, forcing the whole architecture to change direction:

  1. 01

    WebView's fetch was unreliable — the exact same code worked fine in a regular browser, but would silently fail inside the screen saver environment, with no clear error to catch. Fix: move all data requests to native NSURLSession.

  2. 02

    WebView's text animation would freezerequestAnimationFrame and opacity transitions would lock up in this environment, causing the text fade-in to simply fail. Fix: render text with native AppKit NSTextField, and let AppKit's own animation handle the motion.

  3. 03

    WebView's background frequently failed to render at all — the whole screen would occasionally show up blank, with the background layer not painting. Fix: render the background with native Core Image (Etheral Shadow style: grayscale mask + distortion + noise).

The conclusion from all three failures was a dual-layer native approach: AppKit for text, Core Image for the background, URLSession for data — WebView dropped out of the main path entirely. The cost of this decision was losing the flexibility of iterating quickly with web technology; what it bought instead was genuine stability inside the screen saver environment. It only replaces the screen saver's content — it doesn't change the wait time a user sets in System Settings.

Action · Ship

The project has to be something a friend can actually install — not something that only runs on my own Mac.

Delivery
  • GitHub public: rowanlin801229/overhead-flights — chosen to be public rather than shared privately, because the portfolio's claim is "this data is real," and making the source public lets that claim actually be checked, rather than just asserted.
  • macos/build-and-install.sh: a one-command build-and-install script, designed for "a friend who has Xcode but doesn't know this project's internals" — not an internal tool only I can understand.
  • Three-step README + Gatekeeper handling: since the project isn't signed by a developer account, macOS blocks unsigned software by default, so the README has to spell out exactly how to get past the Gatekeeper warning — it can't assume the user already knows how to handle it.
  • Portfolio assets: the main mockup image, overhead-flights-demo.mp4 (an actual capture of BAW8RM, a flight with a fully resolved route), and a static hero image — chosen because it shows callsign, airline, origin → destination, and altitude/bearing all at once, all real data, not layout placeholders.
Result

A quiet ambient display, not a dashboard.

Outcome

A quiet ambient display: it doesn't compete for attention by interrupting. When a plane is present, it gives a restrained cue; when it isn't, it's just black. It turns "there's a plane overhead" into an environmental fact that can be noticed, never forced on anyone.

Goal Result
Product installs .saver installs; selectable in System Settings' "Customize"
Shows real flights, not simulated ✅ Multiple real callsigns, verifiable against airplanes.live
Polling respects the spirit of the public terms ✅ 90-second polling, below the official 1 req/s limit
Visuals read as editorial typography, not a tracking tool ✅ Near-black background + AppKit text; no map, no list, no terminal green
A third person can install it from the docs ⚠️ README and script are ready; no friend test-install feedback yet
Positioning holds up to challenge ✅ Airframe addressed directly; the difference distilled to medium + aesthetic execution

Limitations: Unsigned, so it triggers Gatekeeper; falls back to a fixed location without GPS; macOS only; adsbdb's exact rate limit has no primary-source number, so the wording stays conservative.

What I learned

  • An unfamiliar runtime environment (legacyScreenSaver) can't be assumed to behave like a normal web stack — technical validation should happen earlier.
  • Not finding a direct competitor doesn't mean you've won. Finding Airframe and still being willing to address it head-on, with an argument that holds up — that's what product judgment actually looks like.
Meta

Role · UI/UX & Product Designer — independently drove direction, market research, and visual/technical trade-offs

AI tools · Claude (ideation / research synthesis / drafting) · Perplexity (verifying primary sources and research) · Grok (review / challenging assumptions) · Cursor (executing approved task packages); switched to manual/web verification when research tools were blocked

Data sources · airplanes.live · adsbdb (credit: PlaneBase / David J Taylor, Edinburgh)

GitHub ↗

← Back to Portfolio
All Projects