Why I Added Ko-fi (And Threw Away the Embed Widget)
Running an indie arcade ad-free means real server bills. Here's why I added Ko-fi, why the official widget script had to go, and how I built a native, adblock-resilient button instead.
If you open up the site header on desktop right now, look down at the developer bio card on the homepage, or slide open the mobile drawer, you'll spot a new emerald green button with a steaming coffee cup icon: Support on Ko-fi. If you've been following Minnich.Awesome for a while, you might know I've been extremely stubborn about keeping this entire place free, clean, and unpolluted.
Adding a tip jar wasn't an impulse decision, and how I ended up building it is a story of its own. It started with a look at my monthly hosting statements, took an uncomfortable detour through ad network requirements, and ended with me throwing away an official JavaScript widget snippet because it was breaking the exact rules I built this website to uphold.
The monthly hosting invoice nobody talks about
When an indie website is just a couple of static HTML files on GitHub Pages, hosting is free and life is simple. But Minnich.Awesome stopped being a static toy a long time ago. Between our Godot 4 WebAssembly games, high score pipelines, user authentication with salted password hashing, cloud save synchronization, daily visit and favorites analytics, and multi-megabyte desktop application downloads, there is real infrastructure ticking away under the hood every second of the day.
Every time someone logs in, syncs their unlocked badges, or opens the daily crate, Firestore runs reads and batched writes. Every time someone downloads one of our standalone desktop tools, bandwidth flows through our server. Add up the Linux VPS instances, database operations, SSL certificates, backup storage, and yearly domain registration renewals, and there is a very real monthly dollar number required just to keep the lights on and the games running smoothly.
Up to this point, 100% of that cost has come directly out of my own pocket. I've happily paid it because building this place is my passion. I love sitting down at night, tweaking physics in Godot, optimizing Flask routes, and watching people play games I created. But as traffic grows and the library expands to over twenty games and half a dozen tools, a solo developer eventually has to ask a pragmatic question: how does this remain sustainable for the next five years without turning into something I resent?
The ad network trap
Earlier this week, I ran a comprehensive site health and ad-readiness audit. If you look back at our commit history around mid-September, you'll see commits tuning privacy policies, elaborating game tips, and verifying Content Security Policies. I did the due diligence. I looked at Google AdSense and third-party arcade ad networks to see what traditional web monetization looked like in 2026.
What I saw was depressing. We all know what modern gaming websites look like today, and we all hate using them. You open a casual browser game to relax for ten minutes during lunch, and your screen is instantly invaded. A sticky banner pins itself to the bottom of the viewport. A thirty-second unskippable video ad blasts auto-playing audio from a floating drawer. A giant cookie-consent modal throws fifty fake toggle switches at you, and just as you reach to click "Start Game", an algorithmic programmatic banner pops in, shifting the entire layout downward by eighty pixels and forcing an accidental misclick.
The whole reason I built Minnich.Awesome was nostalgia for the golden age of the indie web—the era of Newgrounds, Miniclip, and early Kongregate in the mid-2000s, when games loaded instantly, ran smoothly, and felt like handcrafted labor-of-love projects rather than engagement-optimized conversion funnels. Slapping flashing banner ads and telemetry trackers across our arcade cards would have completely destroyed the soul of this site. If keeping the site alive meant making it miserable to visit, the cure would be worse than the disease.
That left voluntary community support. A tip jar model—where the site remains 100% free and open for everyone, but anyone who genuinely enjoys spending time here can chip in three dollars for a coffee—felt honest, respectful, and aligned with what Minnich.Awesome stands for. So I set up a Ko-fi page.
The two-line widget script that broke everything
Once you create a Ko-fi account, their creator dashboard cheerfully hands you an official snippet to embed on your website. It looks innocent enough:
<script type='text/javascript'>
kofiwidget2.init('Support me on Ko-fi', '#2cc774', 'H1Y0278OA5');
kofiwidget2.draw();
</script>
I copied it, pasted it into my local development template, and opened the browser. What followed was an immediate, visceral reminder of why modern front-end web development is so often frustrating.
First was our Content Security Policy (CSP). In webapp/__init__.py, we enforce strict CSP headers. We explicitly disallow rogue third-party scripts from executing in our visitors' browsers to safeguard authentication tokens and protect against cross-site scripting vulnerabilities. Loading an unversioned bundle directly from storage.ko-fi.com immediately tripped browser security alarms. To allow it, I would have had to punch a permanent hole in our security perimeter, trusting an external CDN server with our DOM.
Second was adblocker interference. Most of the people who play indie web games and follow software development run browser extensions like uBlock Origin, Privacy Badger, or Brave Shields. As soon as I tested the official script with a standard adblocker enabled, the entire widget was silently vaporized. Adblockers maintain strict heuristics against remote donation widget trackers. In place of a nice button, users saw awkward empty layout space and a console flooded with network cancellation errors. The exact tech-savvy audience most likely to appreciate indie software wouldn't even see the button at all.
Third was performance and design incoherence. Why on earth do we need twenty kilobytes of JavaScript, external network roundtrips, and runtime DOM injection just to link someone to a URL? Furthermore, the rendered widget was a flat, generic pill button that clashed horribly with Minnich.Awesome's tactile, glassmorphic arcade aesthetic. It looked like an alien sticker slapped onto a vintage arcade cabinet.
Building a native, adblock-resilient arcade button
So I tossed the official JavaScript snippet straight into the recycle bin and built a native implementation from scratch.
At its core, a tip jar button doesn't need script execution. It only needs to be a clean, semantic hyperlink: an <a> tag with target="_blank" and rel="noopener noreferrer". No trackers, no cookies, no third-party CDN dependencies.
Next came the imagery. Instead of loading Ko-fi's logo from an external server where an adblocker might snag it or a CDN outage might break it, I self-hosted the official cup artwork directly inside our repository at static/images/kofi-cup.webp. Because it is served locally alongside the rest of our game sprites, it caches instantly, triggers zero adblocker filters, and renders without any cumulative layout shift.
Then came the fun part: giving it the Minnich.Awesome arcade treatment. In static/css/style-src.css, I engineered the .mc-btn-kofi class. I grounded it in Ko-fi's vibrant #2cc774 emerald green, added a subtle semi-transparent white gloss gradient across the top half to emulate curved arcade cabinet plastic, and gave it an active pressed-down inset shadow.
To make it feel alive, I added a playful micro-interaction: when you hover your cursor over the button, the coffee cup icon rotates slightly by six degrees (transform: rotate(-6deg) scale(1.1)) and bounces slightly on its spring curve, mimicking the motion of tilting a mug for a toast. It's tactile, it's responsive, and it belongs on this site.
Where it lives (and where it doesn't)
Placement mattered just as much as styling. I didn't want the button stalking you across every gameplay viewport or popping up modally when you lose a game.
Instead, I placed it thoughtfully where you'd naturally expect to find it:
- The top header bar: Nestled quietly in the upper right navigation cluster next to the search trigger and theme controls.
- The mobile drawer menu: Tucked at the bottom of the navigation drawer so mobile players have quick access without it blocking screen real estate.
- Developer profile cards: Featured prominently on my bio card on both the homepage and the About page, right alongside my GitHub profile link.
- The global footer: Stationed alongside our changelog and privacy links as a persistent, low-key anchor.
The promise: Free means free
I want to be crystal clear about what this does and does not mean for the future of Minnich.Awesome.
There will never be a "VIP Pass" that gates games behind paywalls. There will never be cooldown timers that tell you to wait twenty minutes to play another round of Chess or Top Ace unless you purchase gems. There will never be locked documentation, restricted tool features, or nag screens asking you to subscribe.
Every game I build, every desktop tool I compile, and every line of code I ship will remain completely free and fully accessible to anyone with a web browser. If you find a game that helps you decompress after a long shift, or a desktop tool that saves you time at your desk, and you want to toss three bucks into the jar to help offset server hosting and database bills, it is genuinely, deeply appreciated.
And if you never contribute a single cent? You are just as welcome here. Play the games, climb the leaderboards, unlock the stickers, and enjoy yourself. That's why I built this place in the first place.
More from the Devlog