home.social

#nerdystuff — Public Fediverse posts

Live and recent posts from across the Fediverse tagged #nerdystuff, aggregated by home.social.

  1. 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
  2. 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
  3. 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
  4. 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
  5. 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
  6. 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:

    1. Dates are weird. Apple stores timestamps as nanoseconds since January 1, 2001 (because of course they do).
    2. 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.).
    3. 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

    1. Save the script as imessage-wrapped.py
    2. Grant Full Disk Access to your terminal (System Settings → Privacy & Security → Full Disk Access)
    3. 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
  7. 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:

    1. Dates are weird. Apple stores timestamps as nanoseconds since January 1, 2001 (because of course they do).
    2. 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.).
    3. 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

    1. Save the script as imessage-wrapped.py
    2. Grant Full Disk Access to your terminal (System Settings → Privacy & Security → Full Disk Access)
    3. 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
  8. 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:

    1. Dates are weird. Apple stores timestamps as nanoseconds since January 1, 2001 (because of course they do).
    2. 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.).
    3. 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

    1. Save the script as imessage-wrapped.py
    2. Grant Full Disk Access to your terminal (System Settings → Privacy & Security → Full Disk Access)
    3. 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
  9. 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:

    1. Dates are weird. Apple stores timestamps as nanoseconds since January 1, 2001 (because of course they do).
    2. 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.).
    3. 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

    1. Save the script as imessage-wrapped.py
    2. Grant Full Disk Access to your terminal (System Settings → Privacy & Security → Full Disk Access)
    3. 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
  10. 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:

    1. Dates are weird. Apple stores timestamps as nanoseconds since January 1, 2001 (because of course they do).
    2. 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.).
    3. 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

    1. Save the script as imessage-wrapped.py
    2. Grant Full Disk Access to your terminal (System Settings → Privacy & Security → Full Disk Access)
    3. 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
  11. 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
  12. 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
  13. 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
  14. 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
  15. 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
  16. 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
  17. 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
  18. 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
  19. 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
  20. 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
  21. 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
  22. 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
  23. 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
  24. 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
  25. 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
  26. 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
  27. 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
  28. 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
  29. 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
  30. 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
  31. 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
  32. 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
  33. 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
  34. 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
  35. 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
  36. 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

  37. 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

  38. 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

  39. My gamechanger this year: finally fixing the nvim-orgmode plugin, and properly reading up on what Org mode can do for you... it's been mind-blowing, and integrates perfectly with Orgzly on my phone.

    #nerdystuff #orgmode

  40. My gamechanger this year: finally fixing the nvim-orgmode plugin, and properly reading up on what Org mode can do for you... it's been mind-blowing, and integrates perfectly with Orgzly on my phone.

    #nerdystuff #orgmode

  41. My gamechanger this year: finally fixing the nvim-orgmode plugin, and properly reading up on what Org mode can do for you... it's been mind-blowing, and integrates perfectly with Orgzly on my phone.

    #nerdystuff #orgmode

  42. My gamechanger this year: finally fixing the nvim-orgmode plugin, and properly reading up on what Org mode can do for you... it's been mind-blowing, and integrates perfectly with Orgzly on my phone.

  43. My gamechanger this year: finally fixing the nvim-orgmode plugin, and properly reading up on what Org mode can do for you... it's been mind-blowing, and integrates perfectly with Orgzly on my phone.

    #nerdystuff #orgmode

  44. I'm just so sick of how commodification and brands is such an ingrained part of the hobbies I enjoy. D&D's monopoly on casual RPG talk; Magic's predatory and FOMO-driven business model; review and award controversies, sales figures and personal scandals in the literary space. I find my passions neutered on arrival these days. I just want to have shared experiences of cool nerdy stuff and connect with people against another backdrop than the consumerist one. #rpg #ttrpg #fantasy #nerdystuff

  45. I'm just so sick of how commodification and brands is such an ingrained part of the hobbies I enjoy. D&D's monopoly on casual RPG talk; Magic's predatory and FOMO-driven business model; review and award controversies, sales figures and personal scandals in the literary space. I find my passions neutered on arrival these days. I just want to have shared experiences of cool nerdy stuff and connect with people against another backdrop than the consumerist one. #rpg #ttrpg #fantasy #nerdystuff

  46. I'm just so sick of how commodification and brands is such an ingrained part of the hobbies I enjoy. D&D's monopoly on casual RPG talk; Magic's predatory and FOMO-driven business model; review and award controversies, sales figures and personal scandals in the literary space. I find my passions neutered on arrival these days. I just want to have shared experiences of cool nerdy stuff and connect with people against another backdrop than the consumerist one. #rpg #ttrpg #fantasy #nerdystuff