Blog

  • A surprisingly good use for AI – remembering games

    A surprisingly good use for AI – remembering games

    I’ve been a gamer all my life and I especially enjoy long, story-driven RPGs. But with long breaks, I’d come back and couldn’t quite remember who all the characters were, where I left off, or which story threads mattered. So I started leaving breadcrumbs for future me, and somewhere along the way, I stumbled onto a use of AI that I hadn’t really considered before.

    The first version of solving this problem was Compadre. I built it as a kind of Notion for gaming – one notebook per game. I jot down a few lines on where I am, who the characters are, and what’s next.

    It also has reminders, which ended up being great for my World of Warcraft habits. Raid resets, dungeon lockouts, mob spawns – all of that little admin. That was the original fix. Just leave enough context so the game is ready for future me without relying on memory alone.

    Coming back to Baldur’s Gate 3

    One of my more recent big story game challenges was Baldur’s Gate 3. Which isn’t new for me. I’ve finished every Mass Effect game (who can forget the end run?), so long games with too many characters and story threads are very much my thing.

    Baldur’s Gate 3 was just the latest one to expose the pattern – long gaps, hazy context and no easy way back in. It’s a massive game with main quests, side quests, companions, character arcs and little threads everywhere.

    In my case, I’d been away from it for about a year. And yes, I built Compadre for exactly this kind of problem, but I also did what people do – I didn’t keep particularly good notes. When I came back, I had the usual “I sort of remember some of this” feeling. I knew I cared about the story, but I didn’t have the full context in my head anymore, so I set out to find another way back in.

    Giving AI the right context

    So I decided to solve this problem with the thing that seems to be on everybody’s mind right now – AI. But before I could ask it anything useful, I had to prime it with where I was in the story. The model knows the whole game. I didn’t.

    I snapped a photo of my quest log on the TV. That gave it my current quests and enough breadcrumbs to know where I was. Then I could ask for what I needed, spoiler-free. Who’s this person again? What story threads should I remember? What have I already done? What was I trying to do?

    Once it had context, the conversation felt natural. With voice, it became even more useful. I could just talk through the things I was fuzzy on and get back into the story without accidentally learning what happens later.

    And it’s not just games. The same approach should work for any long-running story. Prime it with where you are, set some boundaries and let it fill in the gaps. You might even be able to get back into that epic TV series you abandoned years ago without spending half the season trying to remember who everyone is.

    So did I just kill my own product with AI?

    The thought crossed my mind, but I don’t think that’s what happened. Compadre still solves the problem it was built for. Some people will prefer a focused app, some will reach for AI, and I’ll probably do both.

    The more interesting takeaway for me is how useful AI becomes when you give it the right context and clear boundaries. In this case, a photo of a quest log and a simple spoiler-free constraint was enough to turn it into a pretty good gaming companion.

    And who knows? Maybe this isn’t a replacement at all. Maybe some of what I learned here will become an addition to Compadre someday.

  • I finally built Whispermarks, the bookmark problem I never quite solved

    I finally built Whispermarks, the bookmark problem I never quite solved

    Whispermarks is a new project, but the idea behind it is old. Around 15 years ago, I built InBrowser, a privacy-first browser for Android that, for its time, became one of the biggest private browsers on mobile. That’s where this problem first showed up for me.

    The point of InBrowser was that it forgot. Close the app and your session was gone – history, cookies, everything. That was the product, way before incognito even existed on mobile. And there was just one problem. People wanted bookmarks.

    The bookmark problem

    Bookmarks are slightly awkward in a browser designed to forget everything. A normal bookmark is persistent by definition. You save a URL because you want it to be there tomorrow. That makes perfect sense in most browsers, but it never really fitted the privacy model I wanted for InBrowser.

    Bookmarks were also one of the most requested features, and I wanted them too. The easy answer would have been to bolt on a normal bookmark database, but that always felt wrong. If I was going to save something permanently in a privacy-focused product, the privacy part had to come first.

    That’s more or less where the idea for Whispermarks came from – a private place for bookmarks that wasn’t tied to the browser itself. I always thought I’d build it eventually. I just didn’t.

    Same itch, better timing

    I didn’t build it back then because I didn’t think I could build it well enough. Saving a URL is trivial. Building something where the service itself can’t read what you’ve saved is a very different problem. Keys, syncing and recovery all become part of the product.

    If I was going to build it, privacy had to be the foundation – not an option somewhere in settings.

    A lot has changed since then. Whatever you think about crypto, it has made concepts like recovery phrases, private keys and self-custody much more familiar. Fifteen years ago, asking a normal user to safely keep a sequence of recovery words would have been quite an interesting product decision. Today, the pattern isn’t nearly as strange.

    The tooling around cryptography is also much better now. The idea hadn’t really changed, but building the version I actually wanted finally felt realistic as a solo developer.

    Starting with the privacy model

    When I came back to the idea, the first question wasn’t what the bookmark interface should look like. It was what the server should be able to see. The answer was – as little as possible.

    Whispermarks encrypts the vault data locally before it leaves the device. The cloud stores the encrypted data, not the readable contents. The server gets encrypted blobs instead of your actual bookmarks.

    The cryptography is based around AES-256-GCM, with each vault having its own encryption key. Access to those keys comes from a recovery phrase held by the user. If you’ve used crypto wallets, the shape of that will feel familiar – just applied to bookmarks instead of money.

    That also means a vault is more than a folder. It’s its own cryptographic boundary. I can have one vault for personal links and another for a client, and decide where those vaults are available without putting everything into one big bucket.

    Deciding where to start

    The next decision was where to start. I landed on browser extensions, mostly because that’s where bookmarks already live.

    Whispermarks works on Chrome, Firefox and Edge. You can import your existing bookmarks, and more importantly, that vault isn’t stuck in one browser. The same vault can move with you between Chrome, Firefox and Edge. In that sense, portability comes for free. All wrapped in a first-class client experience (imho).

    I spent a lot of time on onboarding. Even if concepts like recovery phrases and keys feel more familiar now, they still need to be introduced properly. Creating an account, saving your recovery phrase, creating your first vault and installing the extension should feel like one flow, not a list of technical chores.

    That’s also why the browser was a good place to start. The site can guide you while the extension is right there. I don’t want someone to feel like they need to understand cryptography just to save a bookmark.

    There will be mobile clients later, but I started with browsers because that’s where I personally save most of my links, and getting that right felt more useful than trying to launch everywhere at once.


    A lot of the products I’ve enjoyed building started the same way. Something annoyed me enough that I wanted my own solution. Whispermarks is one of those, just stretched over a much longer timeline.

    The problem showed up years ago. I left it alone because I didn’t like the version I could build then. This is me finally fixing that old itch, and somewhere along the way, it turned into a product I think is pretty cool on its own.

    Whispermarks is available at whispermarks.com.

  • How I finally fit AI into the way I build software

    How I finally fit AI into the way I build software

    For a long time, I used AI the way I think many developers started using it. Chatting with ChatGPT, asking questions, poking at what it could do. Later, I brought it into my editor, Zed and VS Code, as autocomplete. Then the slow move over to terminal based AI assistance. Useful, sometimes even impressive. But it didn’t feel natural.

    I think part of the problem was how I was thinking about it. AI as the “Lovable model”: one prompt in, an okay-ish app out. Interesting, yes, but I could never wrap my head around that as a serious workflow. A rough app from a single prompt is still a rough app.

    The shift came when I stopped thinking of AI as autocomplete or a one-prompt app generator, and started treating it more like a engineer on my team. Not someone who owns the product, the architecture, or the decisions, but someone who can work extremely well inside a clear frame.

    That’s when it started fitting with how I already work.

    (more…)
  • How AI made leaving Evernote finally worth it

    How AI made leaving Evernote finally worth it

    For about 15 years, Evernote was one of the few apps I genuinely loved. I started with notes, then shifted to using it heavily for tasks and reminders as well.

    (more…)
  • Is the SaaSpocalypse real, or is it just a squeeze?

    Is the SaaSpocalypse real, or is it just a squeeze?

    There is a lot of noise right now about the SaaSpocalypse. Depending on who you listen to, AI is either about to destroy the software industry as we know it, or this is just another market panic dressed up as a technology story.

    I’ve spent close to 30 years building software, and much of that time has been spent building and managing products used by millions of people. So I’m not particularly interested in looking at this as a stock market story, or making dramatic predictions about every large SaaS company suddenly disappearing. I’m more interested in looking at it from the builder’s side.

    (more…)
  • What if the next programming language isn’t for us?

    What if the next programming language isn’t for us?

    We’ve spent decades optimizing programming languages for humans. Nicer syntax, higher-level abstractions, better type systems and better tooling, all in service of human authors. But something is shifting. Increasingly, we describe what we want and AI writes the code.

    If that continues, there’s a question I can’t shake:

    If AI becomes the primary author of software, why would the next programming language still be designed for us humans?

    (more…)
  • Four things wrong with the Evernote Android widget

    Four things wrong with the Evernote Android widget

    I’ve been a paying Evernote user since May 2010. For most of that time I’ve been happy with it. But almost all of my day-to-day use goes through the Android widget rather than the app itself, and the widget hasn’t kept up.

    Four things bother me. None of them are dramatic on their own. Together they add up.

    (more…)
  • How to trigger and run Firebase scheduled functions from localhost

    How to trigger and run Firebase scheduled functions from localhost

    Every wonder how to trigger Firebase scheduled (pubsub) functions directly from localhost during development? It’s actually quite easy, but for some reason not very well documented.

    Start by installing Firebase CLI:

    npm install -g firebase-tools

    Setup local Firebase emulators:

    firebase init emulators

    Make sure that your firebase.json has a pubsub port defined.

    "emulators": {
      ...
      "pubsub": {
        "port": "8085"
      }
    

    Start your local emulators:

    firebase emulators:start

    Shell into the running Firebase emulator instance

    firebase functions:shell

    You can now trigger any functions.pubsub.schedule directly from this prompt. E.g:

    scheduleNotifier();

    You’re now able to significantly improve your firebase function development, since you can trigger the scheduled functions interactively.

    firebase > scheduleNotifier();
    'Successfully invoked function.'
    firebase > >  No matching documents.
    >  Notifier done: 0
    
  • How to check a DNS TXT record using OSX Terminal

    A lot of services today authenticate DNS via TXT records and i find myself looking for web-services that checks those values for me. However, doing this will only give me the DNS record(s) for the DNS where the service is running, not actually my local DNS response.

    Doing this via the Mac/OSX Terminal (command line) is trivially easy:

    dig -t txt domain.com

    Which will give you the TXT record for the specified domain. You can change the “txt” part to any other DNS parameters.

  • Updating PHP to 7.2 on OSX using Homebrew

    Updating PHP to 7.2 on OSX using Homebrew

    PHP 7.2 provides several critical security updates and it’s important for any PHP developer to make sure that they leave the 5.x branch and jump on the 7.x branch as soon as possible.

    Updating to PHP 7.2 is a breeze If you’re on OSX and using Homebrew.

    Start by making sure that brew is up to date:

    brew update && brew upgrade

    Unlink any previous PHP installation (this may vary, so you might need to replace the php71 accordingly):

    brew unlink php71

    Install PHP 7.2:

    brew install php72

    Once done, you can verify that PHP 7.2 was installed by using php -v

    Happy coding!