Remember my Bulls & Cows game? The one with no frameworks, no AI, just vanilla JS? Well, I made it installable. Like, "put it on your home screen and play offline" installable.
No App Store. No Google Play. No $99/year Apple Developer fee. No $25 Google registration. Just... a web app that acts like a native app.
And apparently, people like it? 1.3K unique players, 1.4K sessions, and 1.8K pageviews — with people spending almost 2 minutes per visit just thinking through a logic puzzle. That's enough traction that I gave it its own home.
🎮 Play it: picasyfijas.com
✨ What's a PWA, Really?
A Progressive Web App is just a regular website with two extra files that tell the browser: "Hey, I can work offline. Let me live on the home screen."
That's it. Two files. Let me show you.
1️⃣ The Manifest (Your App's ID Card)
{
"name": "Bulls & Cows - Picas y Fijas",
"short_name": "Bulls & Cows",
"start_url": "/",
"display": "standalone",
"background_color": "#EBEBD3",
"theme_color": "#4281A4",
"icons": [
{ "src": "icon-192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "icon-512.png", "sizes": "512x512", "type": "image/png" }
]
}
This tells the browser what to call your app, what icon to use, and that standalone means "hide the browser chrome and look like a real app."
Link it in your HTML:
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js');
}
2️⃣ The Service Worker (Your Offline Brain)
const CACHE_NAME = 'Bulls-and-Cows-v4';
const FILES_TO_CACHE = [
'/', '/index.html', '/styles.css', '/script.js',
'/manifest.json', '/icon-192.png', '/icon-512.png'
];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => cache.addAll(FILES_TO_CACHE))
);
});
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request)
.then((response) => response || fetch(event.request))
);
});
That's the entire service worker. It caches your files on install, then serves them from cache when offline. No npm packages. No build step.
Register it in your HTML:
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js');
}
3️⃣ The Install Prompt
Browsers show an install button automatically, but I added my own banner because I wanted control over the UX:
let deferredPrompt = null;
window.addEventListener('beforeinstallprompt', (e) => {
e.preventDefault();
deferredPrompt = e;
document.getElementById('install-banner').style.display = 'flex';
});
document.getElementById('install-btn').addEventListener('click', () => {
deferredPrompt.prompt();
});
💰 Why Not the App Stores?
Apple charges $99/year. Google charges $25 once. Both require review processes, screenshots, privacy policies, and jumping through hoops.
My game collects zero data. No accounts. No ads. Just privacy-friendly Plausible for basic stats. It's literally just a logic puzzle.
PWAs let me skip all of that. Users get an installable app. I keep my $124. Everyone wins.
🎯 The Result
- ✅ Works offline
- ✅ Installable on Android, iOS, and desktop
- ✅ No app store fees
- ✅ Updates instantly (no waiting for review)
- ✅ Still just HTML, CSS, and vanilla JS
Total new code: ~50 lines.
👉 Play it: picasyfijas.com
👉 Source: github.com/Yosolita1978/bullsandcows
Have you turned a side project into a PWA? I'd love to hear about it 👇
Top comments (0)