How Wordle kept your streak alive when it migrated to the NYT's website

timothee

Timothée Boucher

Posted on February 14, 2022

How Wordle kept your streak alive when it migrated to the NYT's website

If you've been online even just one day in the past few weeks, you've probably heard of Wordle, a word-game that caught the world, and Twitter in particular, by surprise with cryptic grids of colored square emoji.

It got so popular that after a few weeks, the New York Times bought the game to add it to their suite of daily word games.

Josh Wardle, Wordle's creator, had announced that he "was working with [the NYT] to make sure wins and streaks will be preserved".

Last week, the game migrated to the NYTimes' domain and I was curious how they migrated the statistics. It turns out that it's pretty simple. :)

The original game lived at https://www.powerlanguage.co.uk/wordle/

If you curl that URL today, you get this:

$ curl -D /dev/stdout https://www.powerlanguage.co.uk/wordle/
HTTP/2 200
date: Mon, 14 Feb 2022 16:57:50 GMT
content-type: text/html
last-modified: Sun, 13 Feb 2022 22:37:30 GMT
[...skip...]
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Wordle - The New York Times</title>
    <meta http-equiv="cache-control" content="no-cache" />
    <meta name="description" content="Guess the hidden word in 6 tries. A new puzzle is available each day" />
  </head>
  <body>
    <script type="text/javascript">
      const ls = window.localStorage;
      // Ensure we're always sending something
      let stringifiedData = '{}';
      // Default to production
      let url = 'https://www.nytimes.com/games/wordle'

      // Don't attempt unless our user has local storage enabled
      if (ls) {
        const errors = [];
        const localData = {
          time: new Date().getTime(),
          statistics: null,
          darkTheme: null,
          colorBlindTheme: null
        };

        // Attempt to pull and parse the stats and themes
        try {
          localData.statistics = JSON.parse(ls.getItem('statistics'));
          localData.darkTheme = JSON.parse(ls.getItem('darkTheme'));
          localData.colorBlindTheme = JSON.parse(ls.getItem('colorBlindTheme'));
        } catch (e) {
          // Anything that's not valid JSON will not be sent
          errors.push(e);
        }

        try {
          stringifiedData = JSON.stringify(localData);
        } catch (e) {
          errors.push(e);
        }
      }

      // Everyone will redirect, regardless of local storage
      window.location.assign(`${url}?data=${stringifiedData}`)
    </script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

It serves a simple HTML document with an inline script that grabs all the statistics of the original game from localStorage, put all the important data in the URL, and forwards the browser to the nytimes.com URL.

Once that page loads (there's a 301 permanent redirect to index.html but that doesn't do much), that data is parsed and saved locally again with this code:

function os() {
    if (localStorage) {
        try {
            var e = new Proxy(new URLSearchParams(window.location.search),{
                get: function(e, a) {
                    return e.get(a)
                }
            });
            if (e.data)
                !function(e) {
                    if (!e.statistics)
                        throw new Error("User local data does not contain statistics. Aborting transfer.");
                    if (ns(e.statistics, e.force)) {
                        localStorage.setItem('nyt-wordle-statistics', JSON.stringify(e.statistics));
                        var a = e.darkTheme;
                        window.themeManager.setDarkTheme(a);
                        var s = !!e.colorBlindTheme;
                        window.themeManager.setColorBlindTheme(s)
                    }
                }(JSON.parse(e.data))
        } catch (e) {}
        window.history.replaceState({}, document.title, new URL(location.pathname,location.href).href)
    }
}
Enter fullscreen mode Exit fullscreen mode

You can see in the call to the ns function that you can force an override of the data. By default, ns short-circuits the data transfer when it finds local data.

And if you do force it by passing "force": true, ns looks at the given statistics, and makes sure you played at least one game but also more games on the original game than the new site.

Finally, note the call to window.history.replaceState. This strips all the data passed in the URL. Using replaceState makes sure that clicking "Back" in your browser won't bring back all the transfer data in the URL. That makes it less likely that someone will share that URL with somebody and erase their statistics by accident.

And that's it!


There IS one part that I haven't quite figured out while looking at this.

I play Wordle on my phone and the old URL was saved in my history so I would just type po and the rest would show up. After the transfer, it stopped populating completely as if I had never been to https://www.powerlanguage.co.uk/wordle/.

But looking at the steps above I don't know why it would just disappear from my local history. There's a 301 in the process, but it's from https://www.nytimes.com/games/wordle?data=... to /games/wordle/index.html?data=....

I'd love to hear if you have ideas about that.


P.S.: I'm aware that the transfer had some bugs for some people, but I believe it was all fixed up.

💖 💪 🙅 🚩
timothee
Timothée Boucher

Posted on February 14, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related