← Work

Soothe

A native iOS proof of concept that pats you until your stop is near, then wakes you kindly. A 2026 rebuild of my 2020 case study. Every ride is simulated.

Year
2026
Track
built
Category
case-study

Tools

  • Swift
  • SwiftUI
  • SwiftData
  • Core Haptics
  • AVAudioEngine
  • Claude Code

Soothe lets a commuter rest on the way home. A gentle pat, felt, heard and seen off one clock, says everything is fine until the stop is near. Then a clear, kind “Time to fly.” wakes them. It is a native iOS proof of concept: every screen is playable, and every ride, stop and signal is simulated. No location, no network, no real timetables. I made it as a product design case study in February 2020 and rebuilt it as an app in September 2026, with Claude Code doing the building one step per session.

Hear the pat

The ride

Today, headed Good evening, with the line Rest. We'll keep watch. Under Next ride a card reads Home after the night shift at 7:40, Zuid to Grootzeil, 26 minutes, one change, with chips for metro 52 and bus 34, and a Start button. Under Your routes, Ferry to Centraal and Saturday market.
The next ride is the saved route leaving soonest.
The route Home after the night shift. A dark map of Amsterdam with the line drawn from Zuid in the south to the north, then Zuid to Grootzeil, 26 minutes, one change. Under The way, metro 52, Zuid to Noord, seven stops, 17 minutes, and a Start button.
A muted map. The underground stretch is drawn dotted.
The ride begins. The route map sits at the top with a Timeline button, and below it a pale round orb inside a darker ring. Settling in. Rest. We'll keep watch. A bar at the foot holds Sound, Touch, Eyes closed and End.
The first minute is always calm.
The timeline of stops, Zuid, then Europaplein marked next, then De Pijp and Vijzelgracht, each marked underground. Under it the orb. Rest. 10 stops to go. Europaplein next.
The card shows the map or the timeline of stops.
Eyes closed. The screen is almost black, with the orb dimmed to grey in the middle and one line at the foot: Eyes closed. Tap to open.
Eyes closed keeps the screen on, since haptics only play in the foreground.
The map faded down to the route line alone. Rest. 6 stops to go. Underground. Counting stops by motion.
The copy says what the app is doing. Here it is simulated.
The second leg, Noord to Grootzeil, with Station Noord, G.J. Scheurleerweg marked next, Oosterlengte and Grootzeil. The orb has turned a warm beige. Change at Noord. Bus 34 next. 3 stops to go.
At the change the pat firms up and the orb turns warm.
The same leg with Grootzeil marked your stop, and the warm orb. Your stop is near. 1 stop to go. Grootzeil is your stop.
Nearing, from the wake distance out: firmer and about 15% faster.
The wake. A bird in flight inside a grey circle. Time to fly. This is Grootzeil. One wide button at the foot reads I'm awake.
The wake runs until I'm awake, whatever the soft settings say.
Welcome back. You rested 18 minutes. Home after the night shift. How was the wake, with Early, Just right chosen, and Late. How do you feel, with Rested, Okay chosen, and Restless. A Done button at the foot.
Demo data, seeded in the app. No real rides.

The whole ride, silent. The sound on this page is Hear it, further down.

Where it started

The 2020 brief asked: have you ever napped on the bus and woken up at the last stop? How do you wake a passenger just before their stop? I answered with saved routes, a tracking screen, statistics, reassuring notifications along the way and a vigorous alarm at the end. The persona was Sıla, an academic in İstanbul who worked overnight three times a week and slept on the second vehicle home, anxious every time.

In 2026 she became Noor, a night-shift nurse in Amsterdam-Noord who rides home exhausted and never lets herself sleep, afraid of the end of the line. A persona, not a research participant. Around her, the 2020 answer was taken apart:

  • Routes, Track, Statistics and Settings became Today, Journey, Rest and You.
  • Reassurance notifications became the Pat: haptic, sound and light on one clock.
  • A pulsate rhythm and white noise became three rhythms, Heartbeat, Pat and Tide, and three ambiences, Rain on the canal, IJ wind and Brown noise.
  • The vigorous wake alert became Nearing, firmer a few stops out, and then Arriving: “Time to fly.”, until “I'm awake”.
  • Badges became Moments, gentle milestones that are never gamified.
  1. Brainstorming, phase 1

    • Feelings13

      • Boring
      • Lethargic
      • Nausea
      • Tiresome
      • Depressive
      • Claustrophobic
      • Comfortable
      • Negative
      • Mechanical
      • Anxiety
      • Mundane
      • Familiar
      • Unfamiliar
    • About Solution12

      • Childhood
      • Multi-Platform Smartwatch
      • Desmond Morris
      • Process Progress
      • Maternal Patting Reassurance
      • Chirping
      • Haptic Feedback
      • Womb
      • Headphones
      • Cradle
      • Multi-vehicular Situations
      • Multi-sensory reassurance at every stoptyped
    • About Travel18

      • A Pack of People
      • Dark and Dim
      • Dusk
      • (Sleeping on bus) Effective
      • (Corporate Service) Grouping
      • Cars
      • Complicated
      • Range
      • (Corporate Service) Hit or Miss
      • Read the Plate
      • Alot Start
      • Crowded
      • Possible
      • System
      • Anxiety
      • Busy Mind
      • Concrete
      • Awareness
    Brainstorming, phase 1: feelings, the solution and the travel.
  2. Handwritten notes on orange. Chirp to give assurances, leading to Desmond Morris, Intimate Behaviour, and a mother's patting action. Then go with childhood actions: why do we sleep on moving vehicles, it is like a cradle, like a womb. Boxed: app pats you time to time to assure you everything is OK, until it is time to fly, then it warns you vigorously.
    The sketch notes. The pat, and time to fly, are both here.
  3. Main user flow

    User context
    User gets out at the usual hour, walks to the stop she has been gotten on the bus.
    Tech context
    User's device has adequate battery power. Cell coverage is abundant for the first phase.
    1. Phase 1, bus

      1. App starts to listen geolocation due to the time pattern of the user.

        • Clock data initiates
      2. App taps into the location sensors and confirms the location is start point of the commute.

        • Location data confirmed
      3. According to the automation rules defined by the user (user opted for sound, haptic & white noise) starts as soon as user connects headphones.

        • Listening location data
      4. Throughout the first phase of the commute, user hears slow, rhythmic sound and feels haptic feedback simultaneously & synchronized.

        • Listening location data
      5. User gets close to the stop to get off the bus and take a train. Now app grasp the situation through location services and changes auditory and haptic feedback to a vigorous rhythm to notify user: time to get off!

        • Location data confirmed
        • Clock data registers
    2. Phase 2, train

      1. User gets into the subway station. Cell coverage lost. App transitions to accelerometer mode.

        • Location data N/A
        • Cellular signal lost
      2. By listening accelerometer data, app grasps train's initiation of movement.

        • Listening accelerometer data
      3. App counts the stops of the subway utilizing accelerometer data. Throughout the commute, app reinitiates soothing feedback until it is time to get off.

        • Listening accelerometer data
      4. User gets close to the stop she needs to exit, this time using accelerometer data and confirm from clock; changes auditory and haptic feedback to a vigorous rhythm to notify user: time to get off again!

        • Accelerometer data initiates
        • Clock data confirmed
      5. As soon as there is location data to confirm the commute is over, app disengages and registers commute to be found in statistics.

        • Location data confirmed
    The main flow: location on the bus, the accelerometer on the train.
  4. Seven hand-sketched wireframes on grey, linked by dashed orange lines. On top, the route list with two routes and Start buttons, editing a route with Auto Start and From and To fields, and a new route. Below, statistics with a bar chart by day, week, month and year, the tracking screen as a vertical line of stops with the current, elapsed and arrival times, and the Sounds and Haptics settings with rhythm and intensity controls.
    Wireframes, sketched in Concepts and tested in Marvel Pop.
  5. A hand holding a phone in a dim room. On screen, the sketched Track Mode, a line of stops with a close button, and the current, elapsed and arrival times in the corner.
    The Track Mode sketch, on a phone.
  6. The XD screens as a mockup on a dark photograph of İstanbul's Tünel. In the middle, an iPhone on Route 01, a line of stops from Taksim over the M2 to Yenikapı, then Marmaray, with Üsküdar lit in blue, and the current time and arrival at the foot between Sounds and Haptics. Beside it, a statistics card with a profile photograph, 93 commutes and a weekly bar chart, and the Sounds sheet with rhythm, intensity and a White Noise choice of Ocean, Rainbow or Forest. The learned routes and a route's settings peek in at the edges.
    The XD screens: one colour for actions, Gill Sans throughout.

What commuters told me

A short questionnaire in 2020 asked about the commute to work and back. For three words on it, most people wrote negative ones: boring, tiring, prolonged. Sleeping through it makes sense. Four findings shaped the app, each with its 2026 answer under it. The board printed no sample size, so I give none.

From my February 2020 questionnaire. Shares of answers as the board printed them. It gives no sample size.

  • Sleeps on the way, share of answers
    AnswerShare
    Yes, often24%
    Sometimes19%
    Rarely10%
    No47%

    More than half of the people I asked sleep on public transport.

    2026Soothe is made for that rest. Its first words are “Rest. We'll keep watch.”

  • Missed a stop asleep, share of answers
    AnswerShare
    For a few times18%
    Rarely2%
    No80%

    One in five had missed a stop because they were asleep.

    2026So the wake ignores the rider's soft settings, and only “I'm awake” ends it.

  • How they commute, share of answers
    AnswerShare
    Company service35%
    Bus or metrobus27%
    Subway or tram22%
    Minibus5%
    Cab4%
    Ferry1%
    Other5%

    After company shuttles and buses came the subway and tram, where some riders have no signal.

    2026So underground, the design counts stops by motion. In this build that is simulated, with no sensors.

  • Headphones, share of answers
    AnswerShare
    Wear them (music, podcasts)37%
    Could (social media, web, games)31%
    Don't32%

    Almost 70% already wear headphones on the way, or could.

    2026So the pat can be heard as well as felt: a low pulse tone, made in code and not played from a file.

A pat, not an alarm

The idea came from Desmond Morris. In Intimate Behaviour he describes how a mother's repeated pat tells a baby there is no danger, the same kind of signal a bird gives with its head before it flies. “The repeated patting is therefore doubly soothing.” (Morris, D. (1997), Intimate Behaviour, New York: Kodansha America, p. 24.)

My 2020 sketch notes turned that into one line: the app pats you from time to time to say everything is OK, until it is time to fly. What changed in 2026 is what happens then. In 2020 it warned you vigorously. Now it says “Time to fly.” and waits for “I'm awake”.

The 2020 flow asked for sound and haptics “simultaneously and synchronized”. In the app one clock emits the beats, and haptics, sound and the orb follow the same emission, so they cannot drift. The sound is made in code, so there are no audio files. The phases change the feel, not only the words: resting is soft, nearing is firmer and about 15% faster, and arriving is a rising pulse with two knocks every two seconds until the rider taps “I'm awake”. The wake ignores the rider's soft settings on purpose. Hear it plays one bar here, made the way the app makes it.

Heartbeatlub · dub, a 1-beat bar
Patpat pat pat · ·, a 5-beat bar
Tideone 3-beat swell, a 4-beat bar
Wakea rise and two knocks, a 2-beat bar at 60 bpm
One beat grid at 60 beats a minute. A rhythm is a bar measured in beats, so one tempo scales every rhythm alike. Nearing runs the bar 15% faster and firmer; arriving swaps in the wake.

Two sheets change the feel mid-ride. Haptics can only be felt on a real iPhone, and I have not verified them on one yet.

The Touch sheet. Under Rhythm, Heartbeat, two soft pats like a resting pulse, then Pat, three pats then a pause, ticked, then Tide, a slow swell in and out. Sliders for Tempo, slower to faster, and Intensity, soft to firm. A Feel it link at the foot.
One tempo scales all three rhythms alike.
The Sound sheet over the ride's map, with Sound switched on. Under Ambience, Just the pat, Rain on the canal, IJ wind, ticked, and Brown noise.
Every ambience is made in code. There are no audio files.

On the first run the pat is felt before the first ride, from the second card.

First run

A crescent moon with three small z's in a circle. Rest. We'll keep watch. Close your eyes on the tram, the metro or the ferry. Soothe counts the stops and wakes you before yours. A Next button and a Skip link.
The orb. The Pat. A gentle pat says: don't worry, I'm here. Soothe keeps patting until your stop is near, then wakes you clearly. A Feel it link above the Next button.
A metro train in a circle. Above and below ground. Above ground, Soothe follows where you are. Underground, it counts stops by motion. Here, every ride is simulated.
Headphones in a circle. Make it yours. Choose what you hear and when to wake. You can change both on You. Sound offers Just the pat, chosen, Rain on the canal, IJ wind and Brown noise. Wake me offers 1 stop before, 2 stops before, chosen, and 3 stops before. A Begin button.

The ride home

The demo ride is Home after the night shift: metro 52 from Zuid to Noord under the IJ, a four-minute change, then bus 34 to Grootzeil. Twenty-six minutes, one change, ten stops. Three rules place the phases. The first minute is always calm. Nearing starts when the stops to go reach the rider's wake distance, one to three stops. Arriving starts 45 seconds before the destination, and only “I'm awake” ends it. Underground, the copy says what the app is doing: “Underground. Counting stops by motion.” In this build that is simulated too.

Home after the night shift

Zuid → Grootzeil · 26 min · 1 change · 10 stops

Drawn by stops, not minutes. Dotted where the metro runs underground.

  1. Metro 52 7 stops · 17 min
  2. Change 4 min
  3. Bus 34 3 stops
  1. Zuid
  2. Europaplein, underground
  3. De Pijp, underground
  4. Vijzelgracht, underground
  5. Rokin, underground
  6. Centraal Station, underground
  7. Noorderpark
  8. Noord
  9. Station Noord
  10. G.J. Scheurleerweg
  11. Oosterlengte
  12. Grootzeil
  1. Settling, resting pat
  2. Resting, resting pat
  3. Underground, resting pat
  4. Resting, resting pat
  5. Transfer, nearing, firmer
  6. Resting, resting pat
  7. Nearing, nearing, firmer
  8. Arriving, the wake
  9. Arrived, the wake
  • Resting pat
  • Nearing, firmer
  • The wake
Settling
The first minute is always calm.
Nearing
Starts 2 stops before Grootzeil, the wake distance on this ride.
Arriving
Starts 45 seconds before the stop. Only “I'm awake” ends it.

Calm, on purpose

The design system is a set of refusals. Matte paper, one primary action per screen, one slow curve with no bounce. No gradients, no drop shadows. Liquid Glass only on the two controls that float over the map. New York for display, SF Pro for the interface, Dynamic Type text styles only. If a screen feels busy, something is removed.

Contrast is a test, not an intention: ink at least 7:1 on paper, secondary text and line chips at least 4.5:1. Two values failed when measured and were changed. Both are on the palette below.

  • paper#F2EFE1Background
  • paperDeep#E8E4D3Cards
  • brand#4B5D7EThe icon's ink: orb, route line, primary button5.8:1 on paper
  • ink#3D4C70Text7.4:1 on paper6.7:1 on paperDeep
  • inkSoft#59637FSecondary text5.2:1 on paper4.7:1 on paperDeep
  • glow#D8A866Warmth, nearing and arriving only
  • rest#8FA28ARested
  • night#0A0D14Eyes closed
  • paper#141A2ABackground
  • paperDeep#1C2438Cards
  • brand#A9B8DAThe icon's ink: orb, route line, primary button8.7:1 on paper
  • ink#ECE7D8Text14.0:1 on paper12.5:1 on paperDeep
  • inkSoft#A7B0CCSecondary text8.0:1 on paper7.2:1 on paperDeep
  • glow#E2B878Warmth, nearing and arriving only
  • rest#9DB39ARested
  • night#0A0D14Eyes closed

Two corrections, after measuring

  • inkSoft, secondary text on a card

    Zuid → Grootzeil · 26 min · 1 change, #5F6A88 · 4.2:1, under AA
    Zuid → Grootzeil · 26 min · 1 change#59637F · 4.7:1
  • Line chips on a card

    Metro 52, Paper on solid · 2.9:1
    Bus 34, Paper on solid · 2.2:1
    Metro 52Ink on a 30% tint · 5.2:1
    Bus 34Ink on a 30% tint · 5.5:1

Light and dark on every screen, both from the same tokens.

  • Today in light mode, navy type on cream paper. Good evening. Rest. We'll keep watch. A card for the next ride, Home after the night shift at 7:40, Zuid to Grootzeil, 26 minutes, one change, and a navy Start button. Under Your routes, Ferry to Centraal and Saturday market.
    Light
    Today in dark mode, cream type on deep navy, the same cards a shade lighter than the page, and a pale blue Start button.
    Dark
  • The ride in light mode, underground. The map has faded to the route line alone, over a navy orb in a pale ring. Rest. 9 stops to go. Underground. Counting stops by motion. A bar at the foot holds Sound, Touch, Eyes closed and End.
    Light
    The same moment in dark mode, a pale blue orb in a navy ring, and cream type on deep navy.
    Dark
  • The wake in light mode. A navy bird in a beige circle. Time to fly. This is Grootzeil. A navy button reads I'm awake.
    Light
    The wake in dark mode. A pale bird in a grey circle over deep navy, and a pale blue I'm awake button.
    Dark

Rest counts the minutes a rider actually rested: settling, resting and underground, never the change or the wake. Moments are noticed, never scored: a first ride, a first rest underground, ten rides. Every ride shown here is seeded demo data, and each screen says so.

Rest. Minutes rested by week, 4 hours 49 minutes in the last 8 weeks, drawn as bars from 3 August to 21 September. Under Median ride, Home after the night shift at 26 minutes over 15 rides, Saturday market at 10 minutes over 5, and Ferry to Centraal at 3 minutes over 4.
Demo data, seeded in the app. No real rides.
Rest, further down. Under Moments, 10 rides with Soothe on 3 September, your first rest underground on 22 August and your first ride with Soothe on 22 August. Under Rides, each ride with its date, minutes rested and how it felt, such as Home after the night shift, 18 minutes rested, Okay.
Demo data, seeded in the app. No real rides.

Accessibility was part of done: VoiceOver throughout, phase changes announced, Reduce Motion turning pulses into opacity, and Eyes closed keeping the screen on because haptics only play in the foreground.

What is simulated

  • Every ride. No location, no sensors. A pure ride model lays the route out in ride time, so a ride lasts exactly the minutes on its card, and a test runs one at 6000× in under a second.
  • Underground. The app counts stops by motion in the copy only.
  • Amsterdam. Station names and line numbers only. Line order checked against GVB's 2026 network, stops from OpenStreetMap, durations computed. No operator is involved.
  • Two switches. Let Soothe learn it and Start with headphones are mocks, and their captions say so.
  • The history. Seeded into an empty store so the charts are never empty. It is not user data.
  • Haptics are not verified on hardware. The Live Activity and the Siri intent have not been checked by eye.
  • No one has used it. No users, no store listing, no research since 2020.

How it was built

Claude Code built it, one step per session, over two days in September 2026. AGENTS.md is the contract every session reads first: the non-negotiables, the workflow, and a router that names the one doc to read for the area being touched. The kickoff prompt is one line: read AGENTS.md and do the next step. A step is done only when it builds, its tests pass and its docs are current. Then the agent stops. It never commits its own step. Starting the next session is my approval of the last one.

  • S0. Scaffold: the agent docs, the XcodeGen project, a Makefile, the icon and an empty three-tab shell.
  • S1. Design system: tokens, components and a hidden gallery.
  • S2. Amsterdam data and routes: the catalog, SwiftData, the seeder, Today, route detail and New route.
  • S3. Feel engine: the rhythm clock, haptics, the procedural soundscape, the Sound and Touch sheets.
  • S4. Journey: the simulator, map and timeline, the orb, the change, underground and the wake.
  • S5. After the ride: the summary, Rest, Moments and seeded history.
  • S6. Onboarding, You, the Live Activity with Dynamic Island, and the Start my ride home intent.
  • S7. Polish: dark mode, Dynamic Type, VoiceOver, Reduce Motion, empty states, copy and screenshots.

Apple frameworks only, no third-party dependencies: 133 Swift files, about 7,050 lines of app code and 2,290 of tests, 205 tests passing, no warnings.

This page was made the same way, one step per session, and I read every word before it landed.

More case studies

See all 27