Native Neocities Hit-Counter Multiplex

Posted: Sat, Feb 10, 2024


I don’t log in to Neocities as much as I should. I tinker away at my site, build it, then just push updates via the command line. From time to time I’ll drop in to see what’s happening, but it’s something I don’t do often enough. The other day though, I logged in, and noticed an update from Dannarchy on his Native Neocities Hit-Counter tutorial. It’s me first time reading it - I never even thought of using the Neocities API to query page hits! I love a hit counter, but didn’t fancy throwing a dart and seeing which random free hit counter on the web would be up for my experiments.

First off - Dann’s tutorial is fab, and I’ll give me thanks as well to Bill’s World for hosting a wrapper for the API that allows for this to work smoothly. If you’re coming here cold, please give Dann’s tutorial a read first, especially if you’re new to JavaScript.

“So, what the hell is a ‘multiplex’ then?"

The answer: to collect view counts for multiple Neocities sites at once!

”… and why would you want to do that? What games of deceit and trickery are at play?"

Well, I’ve migrated Neocities sites twice now. I have profiles still up for surgerywaste and carcercitymall, my previous domains. I’ve been on Neocities since April 2018, which I’m realising is close to 6 years now… not sure how I feel about that. I would’ve had my reasons for why I created fresh sites rather than just renaming what was there, not like I can remember what those reasons are now!

I thought it would be a shame to let those views rot. Those are real people who somehow found out about my lil page after all, even if it was a much older version of it.

If you only have one Neocities site and/or do not want to combine hit counts (you know, like a sane person) then this code is probably useless to you.

Still here? Let’s get into it. Making an acronym of this page title just makes me think of NNHMN, so I’ve got Lush Longing by them playing in the background as I knock this together.


The bulk of this is the same as Dann’s, we’re just making a few tweaks. I’ll dump, then explain.

<span id="hitcount"></span>

<script>
var index = ["lethalcompound", "carcercitymall", "surgerywaste"],
  url = "https://weirdscifi.ratiosemper.com/neocities.php?sitename=",
  reqs = index.map(site => fetch(url + site));

Promise.all(reqs)
  .then(ps => Promise.all(ps.map(p => p.json())))
  .then(res => 
  {
    var totalViews = res.reduce((acc, obj) => acc + obj.info.views, 0),
      viewArray = totalViews.toString().split(""),
      html = "";

    for (i = 0; i < viewArray.length; i++)
    {
      html += `<img src="/img/counter/` + viewArray[i] + `.gif"/>`;

      if ((viewArray.length - 1 - i) % 3 == 0 && (viewArray.length - 1 - i) != 0)
      {
        html += `<span class="sep">,</span>`;
      }
    }

    document.getElementById("hitcount").innerHTML = html;
});
</script>

what the code does

We define a few variables at the start - our list of Neocities sites, the link we’ll be hitting, and then a list of requests.

map() is a function that takes a list of things, does something to each one, and then makes a new list. Here, we’re doing a couple things at once. We’re taking each Neocities entry, tacking url in front of it, then wrapping each of those into a function called fetch().

fetch() is just a more modern version of XMLHttpRequest – the old one’s just fine, but fetch() allows for some nifty things. We want to grab some data from somewhere that’s not my site, and we’ll have to do it 3 times. Each fetch() is like an empty envelope that will head to its destination, grab what’s needed, and come back ready for us to do stuff with. It’s not instant though, we have to wait a bit, even if it’s only a few milliseconds!

Promise.all(reqs) says right – let me wait for these 3 envelopes to go out and come back with something before I do anything else. We can chain our next steps by using .then(...).

Our first then looks a hot mess, but as before we’re just getting our response from each request into a JSON-shaped object so we can do some fun stuff.

Once that has finished, we’re pretty much in the clear. There is a neat little function called reduce() that I’m using, but you can also use a basic for loop if you want. It takes our 3 shiny new JSON objects, represented by res, and accumulates each one’s views. We’ve grabbed the ‘view’ count - you can choose ‘hit’ if you want, but ‘view’ is a more accurate uh, view of who’s visiting your site. That’s got our total views for all 3 sites!

The rest is as Dann’s tutorial plays out. Convert this final number into an array of strings, and then for each digit, add an <img /> corresponding to the digit value to our output HTML string. I’ve also pinched the comma separation bit as well, as a great string of numbers with no separators hurts my soul.

what you’d might like to change

You can stick the <span id="hitcount" /> wherever you like. I’ve put mine right at the bottom of my footer, but you can nest it anywhere your heart desires.

I’ve also wrapped the comma used to separate each group of digits in a <span /> and whacked a class on it so I can style it - for me, styling was as simple as adding some padding, but maybe you want to do some wacky shit. Go wild.

Also watch for the image source. Change <img src="X" /> to where your digit images are located.

Of course, remember to replace the Neocities sites with your own as well!