This is not rocket science, and many of you have probably done it a hundred times. But it solved a real, annoying problem for me in 10 minutes, so I'm sharing it in case it helps someone else. π
The problem
I run PlayCombo, a small site with around 3,000 free HTML5 games split into 13 category pages. The games are loaded from a JSON list and rendered with plain JavaScript inside WordPress.
Every day I post a story or a short video on Instagram, Facebook and YouTube showing one game. Then I add the link... and the user lands on a category page with hundreds of games. The game they just saw? Somewhere in there. Good luck. π
People suggested sorting by date, but I didn't want to change the order of the whole catalog every evening, or move things around by hand.
The idea
Put the game in the URL:
https://playcombo.it/?gioco=pinch-bubbles
When the page loads, a small script reads ?gioco=, finds the matching game in the list that's already in the page, and opens its preview. If the parameter isn't there, the script does nothing.
No backend, no database, no changes to the JSON.
The code
My game engine already exposes two things on window: the game list (listaGiochi) and the function that opens a game preview (apriAnteprima). (Yes, the names are Italian: gioco = game, titolo = title.) The snippet just uses them:
(function () {
// 1. Read ?gioco=... from the URL. No parameter? Stop right here.
var richiesto = new URLSearchParams(location.search).get('gioco');
if (!richiesto) return;
// 2. Turn any title into a URL-friendly slug:
// "Pinch Bubbles" -> "pinch-bubbles", "CittΓ CafΓ©" -> "citta-cafe"
function slug(t) {
return String(t || '').toLowerCase()
.normalize('NFD').replace(/[\u0300-\u036f]/g, '') // strip accents
.replace(/[^a-z0-9]+/g, '-') // anything else -> "-"
.replace(/^-+|-+$/g, ''); // trim dashes
}
var cercato = slug(richiesto);
var tentativi = 0;
// 3. The engine loads asynchronously, so wait until the list
// and the open function are ready (check every 100 ms).
var attesa = setInterval(function () {
tentativi++;
var lista = window.listaGiochi;
if (typeof window.apriAnteprima === 'function' && Array.isArray(lista) && lista.length) {
clearInterval(attesa);
// 4. Exact match first, then a partial match as a fallback.
var gioco = lista.find(function (g) { return slug(g.titolo) === cercato; }) ||
lista.find(function (g) { return slug(g.titolo).indexOf(cercato) !== -1; });
if (gioco) window.apriAnteprima(gioco);
}
// 5. Give up after ~10 seconds so it never runs forever.
if (tentativi > 100) clearInterval(attesa);
}, 100);
})();
Why I like it
-
Zero impact on normal visits. Without
?gioco=, the script exits on the first line. - Separate from the engine. I added it with a "custom JS/CSS/HTML" plugin in WordPress, not inside the engine file. If something goes wrong, the worst case is that the game just doesn't open by itself.
-
Works for every game. No per-game setup: the slug is generated from the title, so
?gioco=air-zeroworks out of the box. - Forgiving links. Accents and capital letters don't matter, and partial names still work.
Possible improvements
- Use a stable
idorslugfield instead of the title, so renaming a game doesn't break old links. - Update the URL with
history.replaceStatewhen a game is opened, so people can share it directly. - Add
og:meta tags per game for nicer previews on social media (that one needs server-side help).
That's it: a small trick, but now every story link opens exactly the game people just saw. If you've solved this differently, I'd love to hear how in the comments! π
Top comments (0)