Announcing WalkUp DJ

WalkUp DJ is now available on the App Store.

It’s a walk-up music app for baseball and softball teams. Create your roster, assign each player a song, and play their entrance music with a single tap during games.

Download WalkUp DJ →

Features

Apple Music Integration — Search millions of songs, preview before you pick, set custom start times so the best part plays first.

Custom Audio — No Apple Music? Upload your own MP3s or M4As. Works with any audio file.

AI Announcer Intros — Generate stadium-style announcements: “Now batting… number 12… SARAH MARTINEZ!” Plays automatically before the walk-up song.

Game Mode — Full-screen batter cards, one-tap play, on-deck preview. Big buttons designed for the dugout.

Game Lineups — Build your batting order, drag to reorder, swap in substitutes mid-game.

Pitcher Warmup Music — Separate warmup songs for pitchers, accessible from a quick panel in Game Mode.

AirPlay Support — Send audio to a Bluetooth speaker, PA system, or any AirPlay device.

iCloud Sync — Keep your teams synced across all your devices.

Share Rosters — Export teams via QR code, file, or link. Import from other coaches instantly.

Pricing

The free tier includes one team with up to 12 players, full Apple Music integration, and Game Mode with auto-stop.

Pro ($9.99/year) unlocks unlimited teams, custom audio uploads, announcer intros, iCloud sync, and export.

Team Pack ($29.99 one-time) includes everything in Pro, forever, with no ads.

Platforms

WalkUp DJ runs on iPhone, iPad, Mac (Apple Silicon), and Vision Pro.

Spring season is here. Give your players the big league experience.

Download WalkUp DJ →

#AI #app #AppDevelopment #Apple #apps #development #iOS #MacIS #MacOS #NerdyStuff #Swift

I Built My Own iMessage Wrapped (And So Can You)

My daughter was joking that someone should build a Spotify Wrapped, but for iMessage — your year in emoji and reactions. My wife said my brother-in-law should build it since he works at Apple. (Every Apple problem is his fault in our house.)

From the other room, my daughter yelled: “I bet Dad could write this!”

Challenge accepted.

The Result

Thirty minutes later, I had a working script. Here’s my 2025:

🎁 iMessage Wrapped 2025 📱 Messages Total: 35,768 Sent: 12,191 Received: 23,577 💬 Reactions Given: 1,974 Received: 5,014 🏆 Your Reaction Style 👍 982 ❤️ 398 😂 275 ‼️ 126 ❓ 19 👎 16 📈 Messages by Month Jan ███████████████ 3,052 Feb █████████████ 2,621 Mar █████████████████ 3,422 Apr ██████████████████ 3,696 May ██████████████████ 3,640 Jun ████████████████████ 3,904 Jul █████████████ 2,561 Aug ████████████ 2,448 Sep ██████████████ 2,790 Oct █████████████████ 3,466 Nov ███████████ 2,206 Dec ██████████ 1,962

Some things I learned about myself:

  • I’m a listener. I receive almost 2x as many messages as I send.
  • People love reacting to me. 5,014 reactions received vs 1,974 given.
  • I’m a thumbs-up guy. 👍 accounts for half my reactions. Apparently I’m very agreeable.
  • June was my chattiest month. December was quietest (holiday break mode).

How It Works

Your iMessage history lives in a SQLite database at ~/Library/Messages/chat.db. It’s just sitting there, queryable.

The tricky bits:

  • Dates are weird. Apple stores timestamps as nanoseconds since January 1, 2001 (because of course they do).
  • Reactions are messages. When you tapback a ❤️, it’s stored as a separate message with associated_message_type set to a magic number (2000 = loved, 2001 = liked, etc.).
  • Custom emoji reactions landed in iOS 17 and are stored in associated_message_emoji.
  • Once you know the schema, it’s just SQL.

    The Script

    Here’s the full thing — about 100 lines of Python, no dependencies beyond the standard library:

    #!/usr/bin/env python3 """ iMessage Wrapped — Your year in emoji and reactions Usage: python3 imessage-wrapped.py [year] Requires: Full Disk Access for Terminal """ import sqlite3 import os import sys from datetime import datetime from pathlib import Path YEAR = int(sys.argv[1]) if len(sys.argv) > 1 else 2025 DB_PATH = Path.home() / "Library/Messages/chat.db" APPLE_EPOCH_OFFSET = 978307200 TAPBACKS = { 2000: "❤️", # Loved 2001: "👍", # Liked 2002: "👎", # Disliked 2003: "😂", # Laughed 2004: "‼️", # Emphasized 2005: "❓", # Questioned } def get_db(): if not DB_PATH.exists(): print(f"❌ Database not found at {DB_PATH}") sys.exit(1) return sqlite3.connect(f"file:{DB_PATH}?mode=ro", uri=True) def date_filter(year): return f""" datetime(date/1000000000 + {APPLE_EPOCH_OFFSET}, 'unixepoch') >= '{year}-01-01' AND datetime(date/1000000000 + {APPLE_EPOCH_OFFSET}, 'unixepoch') < '{year + 1}-01-01' """ def main(): print(f"\n🎁 iMessage Wrapped {YEAR}\n") db = get_db() cur = db.cursor() # Message counts cur.execute(f""" SELECT COUNT(*), SUM(CASE WHEN is_from_me = 1 THEN 1 ELSE 0 END), SUM(CASE WHEN is_from_me = 0 THEN 1 ELSE 0 END) FROM message WHERE {date_filter(YEAR)} AND associated_message_type = 0 """) total, sent, received = cur.fetchone() print(f"📱 Messages: {total:,} ({sent:,} sent, {received:,} received)") # Reaction counts cur.execute(f""" SELECT SUM(CASE WHEN is_from_me = 1 THEN 1 ELSE 0 END), SUM(CASE WHEN is_from_me = 0 THEN 1 ELSE 0 END) FROM message WHERE {date_filter(YEAR)} AND associated_message_type >= 2000 """) given, got = cur.fetchone() print(f"💬 Reactions: {given + got:,} ({given:,} given, {got:,} received)") # Your tapback style print(f"\n🏆 Your Reaction Style") cur.execute(f""" SELECT associated_message_type, COUNT(*) FROM message WHERE {date_filter(YEAR)} AND associated_message_type BETWEEN 2000 AND 2005 AND is_from_me = 1 GROUP BY associated_message_type ORDER BY COUNT(*) DESC """) for type_id, cnt in cur.fetchall(): print(f" {TAPBACKS.get(type_id, '?')} {cnt:,}") # Custom emoji cur.execute(f""" SELECT associated_message_emoji, COUNT(*) FROM message WHERE {date_filter(YEAR)} AND associated_message_emoji IS NOT NULL AND is_from_me = 1 GROUP BY associated_message_emoji ORDER BY COUNT(*) DESC LIMIT 5 """) customs = cur.fetchall() if customs: print(f"\n🎯 Custom Reactions: {', '.join(f'{e} ({c})' for e, c in customs)}") # Monthly volume print(f"\n📈 By Month") cur.execute(f""" SELECT strftime('%m', datetime(date/1000000000 + {APPLE_EPOCH_OFFSET}, 'unixepoch')), COUNT(*) FROM message WHERE {date_filter(YEAR)} AND associated_message_type = 0 GROUP BY 1 ORDER BY 1 """) months = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"] data = {row[0]: row[1] for row in cur.fetchall()} max_cnt = max(data.values()) if data else 1 for i, name in enumerate(months, 1): cnt = data.get(f"{i:02d}", 0) bar = "█" * int(20 * cnt / max_cnt) print(f" {name} {bar} {cnt:,}") db.close() if __name__ == "__main__": main()

    Running It

  • Save the script as imessage-wrapped.py
  • Grant Full Disk Access to your terminal (System Settings → Privacy & Security → Full Disk Access)
  • Run it:
  • python3 imessage-wrapped.py # defaults to 2025 python3 imessage-wrapped.py 2024 # or any year

    That’s it. Your data never leaves your machine.

    What I’d Add Next

    If I turn this into a proper app:

    • Shareable cards — export your stats as an image
    • Conversation breakdown — who do you text the most?
    • Time of day patterns — are you a morning texter or a midnight scroller?
    • Streak tracking — longest daily conversation streak

    But honestly? The script is fun enough. Sometimes a quick hack that makes your daughter laugh is the whole point.

    The script and a slightly more polished version are on GitHub if you want to grab it.

    #Apple #iMessage #Messages #NerdyStuff #Python

    I’m Shipping Like I Have a Team (I Don’t)

    There’s this meme about a kid who got an angle grinder and it “changed his life” — the joke being he’s now cutting catalytic converters off cars. Dark humor aside, there’s a kernel of truth there: the right tool at the right time can completely change your trajectory.

    For me, that tool was Claude with a Max subscription and a handful of CLI utilities. Over the past few months, I’ve built more software than I had in the previous three years combined.

    The Setup

    It’s not complicated:

    • Claude Max: $100/month → now upgraded to the subscription with API access
    • Claude Code: Anthropic’s CLI for agentic development
    • Open Claw: Orchestration
    • A handful of connected tools GitHub, file system, web access

    That’s it. No massive infrastructure. No team. Just me, talking to a model that can actually do things.

    What I’ve Built

    iOS apps. macOS utilities. Web apps. Infrastructure for the Little League I help run. A podcast recording studio. Tools that solve problems I didn’t even know I had until building them became trivial.

    I’m not going to list everything — that’s not the point. The point is that the list exists at all.

    A year ago, most of these ideas would have stayed in my notes app. “That would be cool, but…” followed by all the reasons it wouldn’t happen. Too much boilerplate. Too many APIs to learn. Too many weekends I’d rather spend with my kids.

    Now? I built walk-up music software for my daughter’s softball team because she asked for it. It’s in the App Store. She’ll use it at games. That sentence still feels surreal to type.

    The Unlock

    Here’s what changed: I stopped being the bottleneck.

    Before, I’d have an idea, think “that would take a weekend,” and never start. Or I’d start and get stuck on some tedious part — API integration, UI polish, test coverage — and abandon it.

    Now I describe what I want, iterate on the approach, and let the agent handle the mechanical parts. I focus on *what* to build and *why*. The *how* is a collaboration.

    It’s not magic. The code isn’t perfect. I still review everything, fix edge cases, make judgment calls. But the ratio of “thinking about building” to “actually building” has completely inverted.

    The Compound Effect

    When building is fast, you build more. When you build more, you get better at describing what you want. When you get better at describing, building gets faster. It compounds.

    But here’s what I didn’t expect: the *ambition* compounds too.

    Projects I would have dismissed as “too much work” now feel approachable. Integrations I would have hand-waved away (“I’d need to learn that API…”) happen in an afternoon. The ceiling on what feels possible keeps rising.

    This is what a new era feels like. Not a single breakthrough moment, but a gradual realization that the rules changed and you’re still playing the old game.

    The Angle Grinder Parallel

    That kid with the angle grinder? He suddenly had leverage. A tool that multiplied his physical capability dramatically.

    This is intellectual leverage. I have the same 24 hours everyone else has, but I’m shipping like I have a small team. Ideas that would’ve stayed in my notes app are now real software that people use.

    We’re at one of those inflection points that only becomes obvious in retrospect. The way we build software is changing — not incrementally, but fundamentally. The gap between “I wish this existed” and “I built this” is collapsing.

    And honestly? I don’t think most people have caught on yet. A year from now, this will feel obvious. Right now, it still feels like a secret.

    If you’ve been on the fence about trying AI-assisted development, just start. Pick a small project. Describe it clearly. See what happens.

    You might be surprised what you can build with the right grinder.

    Jake Spurlock builds things in the Bay Area. By day he’s a Forward Deployed Engineer at WordPress VIP. By night he’s apparently become a one-man software studio. He spends too much time thinking about baseball.

    #development #iOS #MacOS #NerdyStuff #WordPress
    WordPress VIP - The leading enterprise content platform

    Build, publish, and scale without limits. WordPress VIP delivers the speed, security, and flexibility enterprises need for measurable results.

    The Leading Enterprise Content Platform | WordPress VIP

    Today Comes to the Mac

    When I launched Today on iOS last October, it was born out of that same Google Reader-shaped hole that never quite healed. A few months later, I kept finding myself reaching for it on my Mac — opening feeds on my phone while sitting in front of a perfectly good laptop. So I did something about it.

    Today – An RSS Reader is now available on the Mac App Store.

    Not Just a Bigger iPhone App

    The easy path would have been to ship the iPad version with Mac Catalyst and call it a day. But that never feels right. A Mac app should feel like a Mac app — keyboard shortcuts, proper window management, menus in the menu bar.

    Today on Mac uses a three-column layout: your feeds in the sidebar, articles in the middle, and the full article on the right. It’s the kind of layout that just makes sense when you have the screen real estate, and it makes triaging a busy morning of feeds feel effortless.

    Keyboard Everything

    This is the feature I keep coming back to. You can navigate the entire app without lifting your hands off the keyboard:

    • J/K to move between articles — borrowed from Gmail, Vim, and the late great Google Reader
    • Space to page through an article, then automatically advance to the next one when you hit the bottom
    • Arrow keys to flip through image galleries
    • Cmd+F to favorite, Cmd+U for read/unread, Cmd+O to pop it open in Safari

    It’s the kind of flow where you can rip through 50 articles before your coffee gets cold.

    Podcasts Get Their Own Window

    On iOS, the Now Playing view is a sheet that slides up. On Mac, it felt wrong — you’d lose your article context. So podcasts open in their own dedicated window that you can move, resize, or tuck into a corner. It opens automatically when you start a podcast and supports chapter navigation, playback speed controls, and keyboard shortcuts (arrow keys to skip, space to pause).

    The SwiftUI of It All

    Bringing an iOS SwiftUI app to macOS is about 80% magical and 20% “why is this happening.” The data layer, most views, and the navigation structure just work. But then you discover that app-level menu commands silently swallow keyboard events before your views ever see them, and suddenly you’re routing key presses through NotificationCenter like it’s 2009. Window management was its own adventure — WindowGroup and @Environment(\.openWindow) are genuinely elegant once you crack the incantation.

    The 80% that just works is remarkable, though. SwiftData, async/await, the entire service layer — zero changes between platforms. That’s the promise of SwiftUI actually delivering.

    What’s Included

    • Native three-column Mac layout with sidebar
    • Full keyboard navigation (J/K, arrows, space, and more)
    • Dedicated Now Playing window with chapter support
    • Podcast playback with adjustable speed
    • AI article summaries (still local-first)
    • OPML import and export
    • Reddit feed support with full media previews
    • Custom accent colors
    • Background sync

    Get It

    Today – An RSS Reader is free on the Mac App Store. If you’ve been using Today on your phone, you’ll feel right at home. If you’re new — welcome. RSS is alive and well.

    P.S. — The iOS version continues to get updates (just landed iPad support) alongside the Mac release. Same codebase, same SwiftData store, same love for the open web.

    #development #iOS #MacOS #NerdyStuff #RSS #Swift #Today

    ScriptStrip: A Simple Transcription App for macOS

    I’ve been on a transcription kick lately. After discovering the built-in transcription features in iOS 26, I started wondering why the same thing didn’t exist on the Mac. I had audio files—meeting recordings, voice memos, podcast clips—sitting on my desktop, and getting them into text meant either uploading to some cloud service or cobbling together a janky workflow.

    So I built ScriptStrip.

    The Idea

    The premise is dead simple: drag an audio or video file onto the app, get a transcript. No accounts, no uploads, no subscriptions. Everything happens on-device using Apple’s speech recognition, so your recordings never leave your Mac.

    What It Does

    • Drag and drop — MP3, M4A, WAV, MOV, MP4, whatever. Drop it in, get text out.
    • On-device processing — All transcription runs locally. Your audio stays private.
    • AI formatting — Transcripts are messy. One click cleans them up—adds paragraphs, fixes punctuation, removes the “ums” and “you knows.”
    • Timestamps — For longer recordings, you can view the transcript with timestamps to jump around.
    • Search and tags — All your transcripts are saved and searchable. I added auto-tagging that pulls out key topics.

    The AI Stuff

    macOS 26 includes Apple’s Foundation Models framework, which gives you access to on-device language models. I’m using it for two things: generating titles from transcript content (so you don’t end up with a list of “Recording 1,” “Recording 2”) and the formatting pass that turns raw speech-to-text output into something readable.

    Why Not Just Use [X]?

    There are plenty of transcription services out there. Most of them upload your audio to their servers, which isn’t great if you’re transcribing anything sensitive. Some charge per minute. Others require yet another account.

    My previous workflow involved firing up Terminal and trying to remember the correct ffmpeg incantation to extract audio from a video file. I’d inevitably forget a flag, hit up ChatGPT, copy-paste the command, and then pipe the result into some other tool. It worked, but it was the kind of friction that made me avoid doing it at all.

    I wanted something that just worked—drag a file, get text. No Terminal, no cloud uploads, no accounts. Just a simple app that lives in my dock and does one thing well.

    Get It

    ScriptStrip is available on the Mac App Store. It requires macOS 26 and works best on Apple Silicon.

    Download on the App Store

    #Apple #Appps #development #iOS #MacOS #NerdyStuff #Swift

    Introducing Jewel Case: Spotify Album Art for Your Mac Desktop

    Remember when music was physical? You’d flip through a stack of CDs, admiring the album art before deciding what to spin. The jewel case was part of the experience—the artwork set the mood before the first note played.

    Streaming killed that ritual. Spotify buried album art behind tiny thumbnails, and unless you’re staring at your phone, you never see it. I wanted that back.

    The Problem

    I spend most of my day looking at my Mac, not my phone. When music is playing, I want to see what’s playing—really see it. The big, beautiful album artwork. Not hunt for a browser tab or pull out my phone.

    macOS widgets seemed like the obvious solution. A glanceable “Now Playing” widget right on my desktop. But Spotify doesn’t offer one. Third-party apps either wanted too many permissions or looked like afterthoughts.

    So I built my own.

    Jewel Case

    Jewel Case is a simple Mac app that does one thing well: shows you what’s playing on Spotify.

    The menu bar shows the current track. Click it, and you get the full album art with playback controls. But the real magic is the widget—drop it on your desktop, and your currently playing album art is always visible. It’s like having a tiny record player on your desk.

    Features:

    • Desktop widget — Small or medium size, shows album artwork and track info
    • Menu bar player — Quick access to playback controls without switching apps
    • Interactive controls — Play, pause, skip—right from the widget (Premium required)
    • Automatic updates — Artwork refreshes as tracks change

    The Technical Bits

    Built with SwiftUI and WidgetKit. The trickiest part was getting the widget to update reliably—widgets don’t have continuous network access, so the main app caches the current track data to a shared App Group container. The widget reads from there instead of hitting Spotify’s API directly.

    OAuth was the usual dance. If you’ve ever implemented Spotify auth, you know.

    Get It

    Jewel Case is available on the Mac App Store — free to download.

    #Apple #development #MacOS #NerdyStuff #Swift #SwuiftUI #WidgetKit

    Celluloid: A Virtual Camera App for macOS

    I’ve had a Logitech C920 webcam for years. It’s a solid camera, but I’ve always hated its color grading — it pushes everything red, making me look perpetually sunburned on video calls.

    Zoom has a “Touch up my appearance” filter that helped a bit, but most of my calls happen in Google Meet, which doesn’t have anything comparable. I wanted a solution that worked across all my video apps — set it once and forget it.

    So I built one.

    Introducing Celluloid – Camera Filters

    Celluloid is a macOS menu bar app that creates a virtual camera with real-time filters and color adjustments. Select “Celluloid Camera” in any video app — Zoom, Google Meet, FaceTime, Microsoft Teams, OBS — and your enhanced video just works.

    Features include:

    • Film-inspired filters — Black Mist (dreamy diffusion), Halation (vintage highlight bloom), Gate Weave (film projector movement), plus classics like Noir and Chrome
    • Color controls — Brightness, contrast, saturation, exposure, color temperature, and sharpness
    • Built-in LUTs — Professional color grades ready to use, including one specifically tuned to fix the Logitech C920’s color issues
    • Battery friendly — The camera only activates when an app is actually using it
    • Privacy focused — All processing happens locally on your Mac. No data collected or transmitted.

    How It Works

    Celluloid uses Apple’s CMIOExtension framework to register a virtual camera device with macOS. The main app captures video from your physical webcam, applies filters using Core Image and custom Metal shaders, then sends the processed frames to the camera extension via CoreMediaIO sink streams.

    The technical journey was interesting — I tried several IPC approaches (file-based sharing, CFPreferences, shared memory) before landing on sink streams, which turned out to be the correct approach for CMIOExtensions running in their restricted sandbox.

    Some technical highlights:

    • SwiftUI for the interface
    • AVFoundation for camera capture
    • Metal-backed CIContext for GPU-accelerated filter processing
    • Custom Metal shaders for the Black Mist diffusion effect
    • CVPixelBufferPool for efficient buffer reuse
    • Darwin notifications for IPC between the app and extension

    Get It

    Celluloid – Camera Filters is available now on the Mac App Store.

    The code is open source on GitHub.

    If you’ve ever wished you could look a little better on video calls without buying studio lighting, give it a try. And if you have ideas for new filters or LUTs, I’d love to hear them.

    #app #development #MacOS #NerdyStuff #Swift #SwiftUI

    Building a Credits Generator for My Daughter’s School Play

    When my daughter’s school was putting on a production of Romeo and Juliet, I wanted to create scrolling credits for the livestream. What started as a simple HTML file turned into a full-featured web app that anyone can use.

    The Problem

    I needed a way to display professional-looking credits during the stream – cast, crew, and acknowledgements – that would work seamlessly with OBS. After manually typing out all the names into an HTML file, I thought: “Why not make this easier for everyone?”

    The Solution

    I built a credits generator that lets you create beautiful scrolling credits with just a web form. No coding required.

    Try it here: whyisjake.github.io/credits

    Features

    • Two Styles: Classic vertical scroll or Star Wars-style 3D perspective crawl (complete with animated starfield!)
    • Full Customization: Colors, speed, and content – all adjustable
    • OBS Ready: Works perfectly as a browser source for streaming
    • Save & Load: Your credits save automatically in your browser
    • Zero Setup: Just open the site and start adding names

    The Star Wars Effect

    The most fun part? Adding the Star Wars crawl option. Using CSS 3D transforms and a canvas-based starfield animation, credits now recede into the distance with that iconic perspective effect. It’s surprisingly satisfying to watch your school play credits disappear into space.

    How It Works

  • Visit the site and fill in your production details
  • Add cast and crew members (with helpful placeholder examples)
  • Choose your style and customize colors
  • Click “Generate Credits”
  • Point OBS at the player URL or download for local use
  • The whole thing is client-side – no server, no database, just localStorage. Perfect for GitHub Pages.

    Use It Yourself

    Whether you’re streaming a play, making a video, or running a production, feel free to use it. It’s free, open source, and requires no account.

    Check it out: whyisjake.github.io/credits

    Built with vanilla HTML, CSS, and JavaScript. Sometimes the best tools are the simple ones.

    #code #nerdyStuff

    Building the Movie & Actor Matcher I Always Wanted

    For years, I’ve had those moments watching a movie where I’d think “wait, didn’t these two actors work together in something else?” or “I wonder what movies both Leonardo DiCaprio and Tom Hanks appeared in together.” Sure, you could dig through IMDb for each actor individually, but there was never a simple tool to just… match them up.

    So I built one.

    What It Does

    Matcher.mov is dead simple: it has two modes that solve the exact problems I always had.

    Movie Mode: Pick two movies, see which actors appeared in both. Ever wonder if any of the same people were in “Inception” and “The Dark Knight”? Now you know in seconds.

    Actor Mode: Pick two actors, see which movies they both appeared in. Finally answered my DiCaprio/Hanks question (it’s “Catch Me If You Can”).

    The Tech Stack

    Built this as a Next.js app with:

    • TypeScript for sanity
    • Tailwind CSS for quick styling
    • The Movie Database (TMDb) API for all the movie/actor data
    • Deployed on Vercel because it just works

    The autocomplete search makes finding movies and actors fast, and everything loads with proper images and links back to TMDb for more details.

    Why This Scratched My Itch

    Sometimes you just need a tool that does one thing really well. IMDb is amazing for deep dives, but terrible for quick “who was in what with whom” questions. It’s also full of ads, and really hard to do simple searches. This fills that gap.

    It’s the kind of utility I’ll bookmark and actually use during movie nights when those random “hey wait a minute…” questions come up.

    Check it out at Matcher.mov and finally get answers to those actor connection questions that have been bugging you for years.

    #BOMB #Code #IMDB #Movies #NerdyStuff #TheGame
    Physicist May Have Solved The Grandfather of All Time-Travel Paradoxes

    What if the past can be changed?

    Yahoo News