DEV Community

Antonio
Antonio

Posted on

A tiny deep-link trick so my Instagram stories open the exact game (3,000 games, 25 lines of JS)

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
Enter fullscreen mode Exit fullscreen mode

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);
})();
Enter fullscreen mode Exit fullscreen mode

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-zero works out of the box.
  • Forgiving links. Accents and capital letters don't matter, and partial names still work.

Possible improvements

  • Use a stable id or slug field instead of the title, so renaming a game doesn't break old links.
  • Update the URL with history.replaceState when 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)