Desktop utility · Mac + Windows · Designed & built end-to-end

The YouTube downloader
that doesn't feel sketchy.

A signed native app — SwiftUI on macOS, Flask + Edge shell on Windows — wrapping yt-dlp, ffmpeg, and deno into one drop-in installer. Quality picker up to 4K, subtitle embedding, in-point trim, "Make compatible" re-encoder, and an inline player. Built for people who aren't command-line people.

Screenshots · live app

See it in action

The real app interface — input card, quality picker, inline AVKit player, and the full download workflow — captured from the macOS build.

View prototype
Reference

Design system

Charcoal base, muted crimson accent, Inter type, native-weight spacing — the full, code-truthful spec for a utility that trusts the OS.

View system
Engineering

Under the hood

SwiftUI + yt-dlp + ffmpeg on macOS. Flask + PyInstaller + Edge WebView2 on Windows. GitHub Actions auto-builds. Self-updating yt-dlp.

See the build
The problem

Every existing option is either sketchy or only for engineers.

Web-based downloaders serve up five ad layers and a malware gamble before the file arrives. Command-line tools like yt-dlp are powerful but completely invisible to anyone who doesn't live in a terminal. The gap was obvious: a clean, honest, native-feeling desktop app that wraps all that power in an interface anyone can use — paste URL, pick quality, click Download, done. No ads, no pop-ups, no shell required.

The flow

One input. Five beats. Everything else is optional.

I sketched the core loop on paper first — the minimum viable journey from "I have a URL" to "I have a file". Everything in the app that isn't on this path is an opt-in extra, not a default.

from paste URL to playable file in five steps
1
paste URL
clipboard
auto-fills on
focus
2
pick quality
up to 4K,
audio-only,
trim, subs
3
download
progress bar,
cancel any
time
4
"Make
compatible"
optional
H.264 re-
encode
5
play inline
AVKit player,
switch subtitle
tracks
clipboard auto-fills — zero typing
re-encode is opt-in. not forced.
Three design bets

The decisions that shaped everything else.

"Native and trustworthy" is easy to say and hard to build. Three early bets turned it from a philosophy into a concrete system — how the app presents itself, how it handles the messy parts, and what the user should never need to think about.

01

Trust the OS, not the brand.

The app speaks macOS and Windows natively — system font rendering, native window chrome, standard controls. No custom titlebars, no Electron quirks. When something looks exactly like the OS, it inherits the OS's credibility. A tool this utilitarian should feel like it came with the computer.

Custom look  →  Native-first everywhere
02

One input, everything else opt-in.

Paste a URL and you're one click from a file. Quality, subtitles, trim, cookies, re-encode — all available but none required. The default path is a single text field and a download button. Power features surface when you need them; they never get in the way when you don't.

All options upfront  →  Progressive disclosure
03

Ship the hardest part: self-updating yt-dlp.

YouTube changes its formats constantly. Rather than releasing a new app build every time, the app ships a yt-dlp self-updater (⌘U). The new binary writes to Application Support, leaving the signed .app untouched. Users stay current without reinstalling.

Reinstall every month  →  ⌘U and done
UX choices that matter

Small calls that change how the whole thing feels.

Most of what makes a utility feel trustworthy rather than janky is a pile of tiny, invisible decisions. Here are the ones I'd defend in a review.

a.

Clipboard auto-fill on focus.

If the clipboard holds a YouTube URL when the app gains focus, it populates the field and immediately starts probing for metadata. Paste-and-go. Zero keystrokes for the happy path — which is almost every launch.

b.

Real file sizes in the quality picker.

Every quality option (240p through 2160p, plus audio-only) shows its estimated combined file size before you click Download. No surprises when a 4K pick turns out to be 4 GB. The probe step fetches the format list and computes sizes upfront.

c.

Auto-translated subtitles, not just passthrough.

When you pick a subtitle language different from the video's source, the app grabs YouTube's server-side translation track and embeds both the original English and the chosen language as separate mov_text tracks — switchable in the inline player.

d.

"Make compatible" as a one-tap escape hatch.

YouTube's preferred VP9/AV1 streams don't play in every player. The "Make compatible" button re-encodes to H.264 + AAC mp4 with a faststart flag, capped at 1080p. No flags, no codec negotiation — one button, universally playable output.

e.

Cancel is always available.

Every download and every ffmpeg re-encode has a working Cancel button. Long-running processes are killed cleanly. This sounds obvious, but most hobbyist wrappers don't wire it up — users end up force-quitting.

f.

Browser cookies to bypass rate limits.

YouTube rate-limits anonymous downloads. The app can use your signed-in session cookies from Chrome, Safari, Firefox, Brave, or Edge — a single picker in the options panel. Authenticated downloads are faster and sidestep the rate-limit block.

Behind the design

One system that looks at home on both platforms.

The app uses a refined charcoal + muted crimson palette — neutral enough to disappear into macOS and Windows, with a single confident accent that never shouts. No brand gradients, no loud chrome. Here's the short version; the full spec lives on the Design page.

Color · Charcoal base · Crimson accent

Neutral enough to trust, confident enough to own.

The macOS app runs in dark mode with a near-black surface (#1C1C1E). The case-study renders those same colours in light mode — cool off-whites and true charcoal, never warm cream. One muted crimson accent does all the work: progress bars, active states, the download button.

Crimson
#C0392B
Primary accent — CTA, active states, progress
Ink
#1C1C1E
Near-black — app window bg, primary text
Off-white
#FAFAF9
Page base — cool white, not warm
Charcoal
#484848
Body text — dark grey on white
Teal
#2E7D6A
Success — download complete, subtitle confirmed
Surface
#F4F2EF
Panel bg — one step darker than page
Type · precise, native-feeling

Inter, because this is a tool, not a product.

Inter 700 Display · headings · 28/700
YouTube Downloader

Precise and confident — the same weight macOS titles use. No rounded oddities, no brand personality bleeding through. Inter 700 looks like it belongs on a desktop.

Inter 400–600 Body · UI labels · controls
Paste a URL, pick your quality, click Download. The file lands in Downloads.

Weight 400 for body, 600 for labels — matching macOS system weight conventions so buttons feel right without custom styling.

JetBrains Mono Eyebrows · progress · file sizes · version tags
yt-dlp 2024.12.13 · 1080p · 847.3 MB

Only where a label needs to read as technical output — version strings, file sizes, download speeds. Same role as in the app's own status bar.

Shape · 4 radii · precise not soft

Precise corners, like native macOS controls.

Radii are tighter than a consumer app — closer to macOS's own control language. Pills for status chips; 16px for cards; 6px for the smallest badges.

Pill Status badges, nav links
999px
Card Input cards, panels
16px
Button Download CTA, controls
12px
Chip File-type badges, quality tags
6px
Identity · the mark

The icon says exactly what the app does.

YouTube Downloader icon
YouTube Downloader
Download arrow on dark square

The icon is a crimson download arrow on a near-black rounded square — unambiguous at 16px in the Dock. No custom shapes, no abstract metaphor. The same icon renders on macOS and Windows. In-product glyphs use SF Symbols (macOS) and standard icon fonts (Windows), both at a consistent stroke weight matched to the Inter body size.

Motion · 4 patterns

System-weight transitions, not animations.

Progress fill ProgressView linear, system tint
done
Completion pulse border glows teal for 600ms
card
Card appear opacity 0 → 1, 220ms ease-out
error
Error shake gentle horizontal shake — standard OS pattern
See the full design spec Get it on GitHub
What's deliberately missing

The cuts I'd defend.

A solo utility has to be ruthless about scope. Here's what I left out on purpose — each cut keeps the app lean, trustworthy, and maintainable without a team.

No playlist UI

The app downloads single videos, not entire playlists. yt-dlp supports playlists internally, but surfacing that in the UI adds a queue manager, progress rollup, and partial-failure handling that wasn't needed for the core use case. The --no-playlist flag is intentional.

No notarization

The macOS .app is ad-hoc signed, not Apple-notarized. Notarization requires a $99/year Apple Developer account. The workaround (right-click → Open → Open) is a one-time friction on first launch — a fair trade for a free personal tool that doesn't warrant an annual fee.

No accounts

No sign-in, no sync, no telemetry. The app stores nothing remotely. Downloaded files land in the system Downloads folder; no database, no cloud backup. Zero sign-in friction — it works fully offline after the initial yt-dlp/ffmpeg bundle ships with the installer.

Windows caveat

The Windows build isn't code-signed with an Authenticode certificate. SmartScreen will warn on first launch; click "More info → Run anyway." A proper EV cert costs $100–300/year from a CA, which doesn't make sense for a free personal tool. The source code is public and the build is reproducible.

Under the hood

One codebase per platform, one philosophy everywhere.

macOS gets a proper SwiftUI app; Windows gets a Flask server wrapped in Edge WebView2 via PyInstaller. Different tech, same UX contract — paste URL, pick quality, download. Both ship with bundled yt-dlp, ffmpeg, and ffprobe so there are zero runtime dependencies for the user.

macOS

SwiftUI · SPM · Signed .dmg

  • Swift Package Manager, no Xcode project required
  • Bundled yt-dlp + ffmpeg + ffprobe in Contents/Resources/
  • Self-updating yt-dlp to Application Support (app signature stays intact)
  • Ad-hoc code-signed .app; .dmg built by build.sh
  • GitHub Actions CI build on every push to main
Windows

Flask · PyInstaller · Inno Setup

  • Flask + waitress backend; Edge WebView2 as the app window (no browser tab)
  • PyInstaller --onefile bundles the entire Python runtime
  • Inno Setup wraps the .exe into a proper Windows installer
  • Same yt-dlp self-update path: writes to AppData
  • Requires Edge WebView2 (pre-installed on Windows 10 21H2+)
Core

yt-dlp · ffmpeg · deno

  • yt-dlp probes format list + downloads video + audio streams
  • ffmpeg muxes streams, embeds subtitles, runs the H.264 re-encode
  • deno handles the self-update fetch on macOS (lightweight, no Python)
  • ffprobe reads container metadata for the inline player
  • AVKit renders the player with subtitle track switching on macOS