a visit counter for neocities using google sheets
ยท 9-minute read
originally i was planning to make a silly game in july that would use google forms embedded in an html form element & google sheets to make a little virtual pet that could be taken care of by anybody who visited the neocities page that had it on it, but partway through making it i remembered there's no cors support unless you have a supporter account & it just didn't feel worth it to go through all of the trouble of using google forms/sheets to make something that was pretty limited in use...
so instead i thought about something i could make that was simpler & smaller that could be embedded in an iframe, & voila, i have made a very simple page hit counter that...
- uses only google sheets & google apps script
- works with non-supporter accounts on neocities!
anyway, here it is...
my google sheets iframe page hit counter
i added a border & background just to make it more obvious where the iframe is, but that can be hidden & styled.
before i explain how it's done, for some general caveats:
-
there are many better ways to do this than using google sheets lol so if using google sheets for purposes it isn't really meant for is not your thing i'd suggest looking up neocities visitor counters & finding one that works out of the box!
-
in general i don't recommend copying random code snippets from online without knowing what they do... make sure you can trust who wrote it!
-
this is a super bare-bones hit counter that just goes up every time somebody loads the page. there are better hit counters out there!!! i just wanted to try to see if i could make one
-
you'll probably want to style it yourself to make it look nicer & this page will not really go into styling that much - it just explains how to count hits & get the number of hits from a google sheet.
with that aside, this is how to make your own:
make your own google sheets hit counter
1. make an empty google sheets file (you can name it whatever)
2. click Extensions > Apps Script - this will create an apps script for your spreadsheet (you can name it whatever)
3. in the Code.gs file that is open by default, delete all the default text in it (it'll be something like function myFunction & type this instead:
function doGet(e) {
// get the hit count as a plain number (as data)
if (e.parameter.api === "numberonly") {
return ContentService.createTextOutput<(getCounter());
}
// get the hit count in an iframe
return HtmlService.createHtmlOutputFromFile("counter")
// this lets the html file be embedded in an iframe on other sites
.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}
function getCounter() {
// the actual counter bit
const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
const sheet = spreadsheet.getActiveSheet();
// highest hit count to show
// you can change 9999 to whatever
const maxCount = 9999;
// log hit count in first column/row cell
const cellCounter = sheet.getRange(1, 1);
// increase current hit count
// don't increase if max
const output = Math.min(cellCounter.getValue() + 1, maxCount);
// set cell to new hit count
cellCounter.setValue(output);
// returns current hit count
return output;
}
i've commented the code above, but i'll explain it here too.
the code lets your apps script either give you html for embedding as an iframe into neocities OR the plain number in case you are on a supporter account & want to style the data in your own way.
the counter is increased every time the script is run (i.e., the page with your script is loaded).
it's set by default to have a max hit count of 9999, but that can be changed to a different number.
4. press the + next to Files & select HTML - name it counter (it will show as counter.html after you name it that)
5. in the counter.html file, delete all the default text & type this instead:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<style>
html {
display: table;
margin: auto;
}
body {
display: table-cell;
}
</style>
</head>
<body>
<label id="counterlabel"></label>
<script>
google.script.run
.withSuccessHandler(function(data) {
const numbers = data.toString();
const countertxt = document.createElement("span");
countertxt.textContent = numbers;
document.getElementById("counterlabel").appendChild(countertxt);
})
.getCounter();
</script>
</body>
</html>
the counter.html file is what will be shown in the iframe. this is just barebones code with a bit of styling to show you the number centred horizontally in your iframe. if you don't want that styling, you can delete the <style> tag & its contents.
now, you'll want to actually style this yourself to make it nicer. you can also use javascript to change what's displayed depending on the number. for example, this is the code i have for the iframe shown on this site:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<link href="https://fonts.googleapis.com/css2?family=Gaegu&display=swap" rel="stylesheet">
<style>
body {
font-family: "Gaegu", sans-serif;
font-weight: 400;
font-style: normal;
}
html {
display: table;
margin: auto;
}
body {
display: table-cell;
}
</style>
</head>
<body>
<label id="counterlabel"></label>
<script>
google.script.run
.withSuccessHandler(function(data) {
const numbers = data.toString();
const countertxt = document.createElement("span");
// max number is 9999
if (data == 9999) {
countertxt.textContent = "๐ฃ" + numbers;
}
// divisible by ten
else {
if (data % 10 == 0) {
countertxt.textContent = "๐ฅ" + numbers;
} else {
// divisible by 2 = even number
if (data % 2 == 0) {
countertxt.textContent = "๐น" + numbers;
}
// not divisible by 2 = odd number
else {
countertxt.textContent = "๐ฐ" + numbers;
}
}
}
document.getElementById("counterlabel").appendChild(countertxt);
})
.getCounter();
</script>
</body>
</html>
in the version on this page, i've changed the font to gaegu & also made it so that a different emoji will be shown next to the number depending on whether it's even/odd, a multiple of 10, or the max number 9999.
6. select Deploy > New Deployment to open the new deployment window & select Select Type > Web App
7. set the web app settings "to Execute as - Me (your account)" & "Who has access - Anyone" then click Deploy
it needs to be executed as you to update the google sheet, & access is for anyone so that anyone can load the iframe on your site & be counted in the hit counter. your google sheet itself will not be directly editable by anybody since it's only viewable by your account. the only thing that anyone can access is your deployed apps script.
8. select "Authorize access" to let the web app be deployed. google will warn you that this web app can access your data & edit it (since it can edit your google sheet).
9. when you get the successful deployment screen, copy the web app url - it'll look like https://script.google.com/macros/s/LONGSCRIPTIDHERE/exec
10. on the page you want to count hits on, include this iframe code, replacing the url with your web app url:
<iframe src="YOUR URL HERE">
</iframe>
you can do a bit of styling for the iframe too - here's how i styled the one above:
<iframe src="YOUR URL HERE" style="background: #ffffff; border: 1px dotted; width: 100px; height: 25px;" scrolling=no>
</iframe>
that's all you need though! & at least at the point of writing this post (2026/08/01) it should work on any neocities site regardless of supporter status.
anyway, i just think it's fun to try to make stuff that you can completely mess around with yourself, & here, since you can decide what is in the counter.html file to show in the iframe, you can make it do all sorts of silly things like
- show a special message if you are the 752nd person to hit the page
- cycle through a zodiac of animals every 12 hits
- make it count down instead of up???
so hopefully you can have a little fun with this simple hit counter like i did here.
for neocities supporter accounts:
i mentioned above that you can get the plain number for easier styling if you're a supporter. here's an example:
times this page has been loaded ๐
the above counter doesn't use an iframe (hence no frame lol) - it just takes the number & displays it using inline css & javascript.
the barebones code to do that is as follows:
<label id="counterlabel"></label>
<script>
const counterurl = "YOUR URL HERE?api=numberonly";
fetch(counterurl).then(response => response.text()).then(data => {
let numbers = data.toString()
var countertxt = document.createElement("span");
countertxt.textContent = numbers;
counterlabel.appendChild(countertxt);
})
</script>
the important part here is that you use the same url except with ?api=numberonly at the end! in Code.gs above, there was a section for this which makes it so that if you add ?api=numberonly to your url, it'll give you the plain data for your hit count instead of the fancy counter.html for an iframe.
you might notice that the code for displaying the number pretty much looks the same as what was in counter.html above & that's because it just uses the same principle (since it's just displaying it through html). but you can style this much more easily since it'll be showing the text directly on your site & not through an iframe! add animations, make it a marquee, make the numbers be displayed as individual images... go wild! (yes, you could technically do a lot of this in the counter.html file too, but i think it's just easier to do it directly on your website without having to update the deployment every time haha.)
this version of the code won't work if you're not a supporter because it uses fetch but if you are a supporter i'd recommend this method instead.
as an aside though when i was trying to figure out why i couldn't embed my script as an iframe i found this which was very helpful:
but is that how you capitalise iframe??? like an apple product??????
thank you always though to the people who reply on stackoverflow 🙏