The workshop
On my desk, every day

MUOOD

Making Use Of Old Device.

The phone in your drawer becomes a live companion screen for your Mac: now playing with synced lyrics, a deck of buttons, Mac controls, vitals, your repos, the weather and a clock. Apple’s StandBy, if StandBy did a lot more — over a USB cable or Wi-Fi.

MUOOD on a landscape phone: a stand-in album cover, ILLENIUM’s ‘Reverie’ with playback controls, and synced lyrics with the current line lit
Role
Design & development, solo
Built for
My own desk
Built
September 2026
Stack
Svelte 5 · Node · SwiftUI

01Why it exists

I wanted a Stream Deck. I had an old phone that did more.

A Stream Deck gives you buttons. Apple’s StandBy gives you a clock and a widget or two. What I actually wanted beside my Mac was both, and more: the song that’s playing and its lyrics, a way to change the volume or kill the mic without hunting for it, and a glance at whether my laptop was struggling.

Meanwhile there was a perfectly good phone sitting in a drawer, doing nothing.

So MUOOD is a web app. The Mac runs a small server; the old phone just opens a page. No app store, no install on the phone — a browser and a cable, or the same Wi-Fi, is enough. Plug it in and the Mac opens MUOOD on the phone by itself.

Hence the name: Making Use Of Old Device.

02The screens

Swipe sideways. Every page is live.

Eleven full-screen pages, each one switchable and reorderable from the Mac. Swipe the phone, use the arrow keys, or pick a page.

Now Playing: album art, title and artist, a seek bar, playback controls, shuffle, volume and repeat, over colours sampled from the cover
Now Playing with synced lyrics: the current line is bright, the lines around it fade
Controls: choose the sound output, mute the microphone, toggle Dark Mode, Keep Awake, Mute and Display Off, and vertical sliders for sound and display brightness
Vitals for a MacBook Pro: CPU at 23 percent with per-core bars, 21.4 GB memory used with normal pressure, battery at 78 percent charging, network, GPU, storage and the busiest apps
Deck: twelve buttons such as Terminal, Code, Browser and Mail; Quiet Mode and Keep Awake are lit; Deploy and Lock Mac need a hold to confirm
Clock: a large 10:24 with the date, the weather and the next meeting, beside a month calendar with today’s agenda
Weather in Jakarta: 31 degrees and partly cloudy, an hourly strip, a seven-day forecast, UV, air quality and sunset
Focus: a pomodoro ring at 14:26 beside today’s task list with two of four done
Today: five and a half hours of screen time, an hourly chart, the week, and time per app
Photos: a landscape picture with the time over it, drifting slowly
Night Stand: a dim amber 23:18, time until sunrise, the moon, tomorrow morning’s weather, the phone’s charge and the first event tomorrow

Real screenshots of MUOOD’s phone UI, running on invented demo data. The song is a real favourite of mine; the cover art is a stand-in.

03Try it

Pull the cable. It doesn’t notice.

The phone can reach the Mac two ways. While it’s on one, it remembers the other. If the current route has been silent for five seconds, it asks both at once and moves to whichever answers first.

    A simulation of the real logic, in real time. The address is an example; the timings — five seconds of silence, a check every three — are the app’s.

    04What it does

    The Mac, one glance away.

    Now playing, with lyrics

    Apple Music in near real time: play, skip, seek, shuffle, favourite. Lyrics are time-synced; tap a line to jump to it. The background takes its colours from the cover.

    Mac controls

    Volume and output device, mic mute, Dark Mode, Keep Awake, Display Off and brightness. A dot appears when the mic or camera is in use.

    A deck of buttons

    Open an app, a link or a folder; run a Shortcut, a shell command or an AppleScript; flip a toggle. Risky ones need a hold to confirm.

    Vitals

    CPU per core, memory pressure, battery, network, GPU, storage and the busiest apps — read from macOS’s own tools, no admin password.

    Repos and deploys

    Local git repos, GitHub CI and Vercel deploys, with a toast when something goes live or fails.

    And the quiet ones

    Clock and calendar, weather, a focus timer, screen time, a photo frame, and a dim Night Stand that warns you if the phone stops charging.

    05How it’s built

    A page, a server, and a helper.

    Three parts, each doing only what it’s good at — and nothing at all while no phone is connected.

    PhoneA Svelte 5 single-page app. One server-sent-events stream in, small POSTs out. Swiping between pages is the browser’s own scroll-snap.
    Mac serverNode with nothing but node:http. Data is split into slices, and a slice is only sent when its JSON actually changed.
    Mac appA SwiftUI menu-bar app that runs the server, restarts it if it crashes, and shows a live preview of the phone.
    HelperA small native Swift program for what Node can’t reach: Core Audio, the calendar, the camera-in-use signal, brightness.
    PairingA 6-digit code or QR, a pairing key, rate limits, and a Host check against DNS rebinding.
    live.svelte.ts — tryOtherRoute()
    async function tryOtherRoute() {
      if (switching || isPreview || !link.key || link.status !== 'offline' || Date.now() - offlineSince < SWITCH_AFTER_MS) return;
      const others = link.via === 'usb' ? savedWifi() : [`http://localhost:${location.port || '80'}`];
      if (!others.length) return;
      switching = true;
      try {
        const base = await Promise.any(others.map(answers));
        if (link.status !== 'offline') return (switching = false); // came back by itself meanwhile
        location.replace(`${base}/#k=${link.key}`);
      } catch {
        switching = false; // nowhere else to go (yet)
      }
    }
    
    setInterval(() => void tryOtherRoute(), 3000);

    06Build notes

    Old hardware is unforgiving.

    01

    A cable that sets itself up

    Every few seconds the Mac checks for Android phones over adb and opens a reverse tunnel, so the phone’s own localhost reaches the Mac. If no MUOOD tab shows up, it opens Chrome on the phone for you, reusing the same tab. Each tab has a stable id, so stale connections get closed.

    02

    Apple Music, without polling it to death

    A long-running JavaScript-for-Automation process listens for Music’s own change notifications, with a light poll to fill gaps. The playhead is sent as a position plus a timestamp, and the phone animates it every frame, corrected for the gap between the two clocks.

    03

    Lyrics that actually match

    An exact lookup on LRCLIB first, then a search with “feat.” and “remaster” stripped, matching the length within four seconds and preferring synced lyrics. Results are cached; the current line is found by binary search and lit 0.35 s early, so it feels on time.

    04

    Kind to slow phones and old screens

    Animations touch only transform and opacity, album colours come from a 24 × 24 thumbnail, and hidden pages stop animating. The whole UI drifts three pixels every two minutes against burn-in, and a blank page reloads itself with a growing back-off.

    Fair print: USB mode is for Android phones, now playing works with Apple Music, and the phone needs a reasonably current browser — old hardware, not an old browser.