Apps Script Systems
Four production tools built on Google Apps Script and Google Sheets, each replacing a process that was being done on paper or by hand. All four run at zero monthly cost.
Campus Tour Operations
Live coordination dashboard for tour groups moving between stations
Campus tour days were coordinated by walkie-talkie and clipboard. Nobody had a single view of where each group was, which station was backed up, or which group had stopped moving — problems surfaced only after a group was already late.
- Sheets API calls removed per interaction
- 3+
- Monthly running cost
- ฿0
- Google Apps Script
- Google Sheets
- HTML Service
How it works
Every tour group is a row. It carries a route, a section, a head count, the station it is currently at, and a status — moving, arrived, or completed. Staff at each station mark a group in and out from their phone; the dashboard adds it up live: total students on site, how many groups are between stations right now, how many have finished, and the state of every station.
The sheets behind it are deliberately boring — Settings, Groups, Students,
EventLog, StationStatus. Anyone on the committee can open the spreadsheet
and read it without me there to explain, which matters more than elegance for a
tool that has to survive me graduating.
The part I am most pleased with
Groups do not report that they are stuck. They just stop moving, and nobody notices until a downstream station is standing idle.
So the system computes how long it has been since each group was last updated and surfaces anything that has gone quiet. It is a small piece of arithmetic that turns the dashboard from a record of what happened into a warning about what is about to.
Measuring before optimising
The first version rewrote the station-status sheet and read it back on every single interaction — three or more Sheets API calls per click, for a value only the client ever consumed and the sheet was never read back for. On a quiet afternoon that was invisible. During an actual event, with staff at every station tapping at once, it was the difference between instant and sluggish.
Computing it in memory and returning it directly removed those calls. That change came from watching the tool be slow while people were using it, not from guessing in advance — and I would not have predicted which call was the expensive one.
There is a second, subtler fix in the same place: dates have to be serialised
before they cross back to the client, because a raw Date in an Apps Script
return value breaks the JSON encoding for the entire response, not just that
field. One unserialised timestamp takes the whole dashboard down.
Nightly Inventory
Barcode-driven stock control for shared committee equipment
Equipment was counted by hand against a paper list at the end of each night. Items went missing without anyone knowing who had borrowed them, and disputes were unresolvable because there was no record.
- Monthly running cost
- ฿0
- Google Apps Script
- Google Sheets
- Barcode generation
- Gmail
How it works
Every item, box, and shelf has a barcode. The system generates the labels, prints them as a sheet, and reads them back through a scanner page that runs in a phone browser — no app to install, which matters when the people scanning change every semester.
Three registries sit behind it: Shelf Registry, Box Registry, and a
Transaction Log that records every movement. Borrowing is a request, not a
handshake: a member picks items, a pickup date and a time slot, and a return
date, and gets back a request ID like BR-20260214-003. Confirmation, approval,
and overdue notices all go out by email automatically.
The point was never the barcodes. It was that "who has the projector" stopped being a question anyone had to remember the answer to.
A schema decision worth explaining
When I added box locations, the obvious place for the column was next to the quantity fields, where it reads naturally.
I put it after Status instead, at the end. The Available column is
formula-based, and several other parts of the system address columns by index —
inserting in the middle would have shifted Available and Status and silently
broken every one of them. The natural-reading position would have cost an
afternoon of debugging something that looked unrelated.
That trade — a slightly worse-looking sheet for a schema that does not break its own dependents — is the kind of judgement I did not have when I started this project and did by reflex by the end of it.
What it replaced
An end-of-night count against a printed list, done by whoever was still around. Items went missing without anyone knowing who had taken them, and there was no record to settle a disagreement with — so disagreements were settled by whoever remembered more confidently.
Committee Registry
OTP-verified member registry with an AI automation suite
Committee membership lived in a Google Form response sheet. Duplicate and mistyped entries accumulated, nobody chased inactive members, and answering a question about the roster meant reading the sheet by eye.
- AI automations in production
- 8
- Monthly running cost
- ฿0
- Google Apps Script
- Google Sheets
- Groq AI
- Discord webhooks
- Gmail
How it works
The registry holds the committee's whole membership lifecycle: intake form, OTP-verified registration, password provisioning, Discord account linking, the org chart, attendance, memberships across more than one organisation, and archival when someone leaves. Two kinds of user — admins who run registration drives, and members who log in occasionally to check their profile or see where they sit in the org chart.
It replaced a Google Form response sheet. Duplicate and mistyped entries accumulated there faster than anyone cleaned them, nobody chased inactive members because nobody knew who they were, and answering a question about the roster meant reading the sheet by eye.
The AI automation suite
Eight automations run in production here, all on Groq:
- Duplicate detection during bulk member import
- Auto-approval scoring for equipment borrow requests
- Automated post-event reports
- Natural-language search across all three Apps Script systems
- Personalised absence follow-up emails
- Predictive inventory demand forecasting
- A chatbot with function calling, on an
/askcommand - Inactivity detection that sends a soft warning rather than a removal
Most of these are decision support, not chat. Forecasting demand and scoring a borrow request are the two that actually removed recurring work — a human still decides, but they start from an answer instead of a blank sheet.
The chatbot is the one people demo and the one I would cut first if I had to choose. Duplicate detection is unglamorous and has probably saved more hours than the rest combined.
Running it for nothing
The whole suite runs on a free tier, with no credit card attached. That was a hard constraint — a student committee has no budget line for API calls, and a system that stops working when a trial expires is worse than no system.
It shapes the design: batch where possible, use the small fast model unless the
task genuinely needs the large one, and never call a model for something a
if statement can answer.
Event Registration
QR check-in, waitlists, and a live attendance display
Event sign-in was a printed name list at a door. Queues formed while staff searched pages by hand, walk-ups had no waitlist, and the final attendance figure was transcribed from paper the following day.
- Monthly running cost
- ฿0
- Google Apps Script
- Google Sheets
- QR codes
- Gmail
How it works
A participant registers through a bilingual form, gets a QR code by email, and is scanned in at the door. Staff run a scanner page on their own phones; a separate live display shows the room filling up in real time. Check-out is scanned too, so the attendance figure is a fact rather than an estimate, and the whole thing exports to CSV when the event is over.
Events open and close, so registration closes itself rather than depending on someone remembering to take the form down.
Details that only show up in a real event
The email rule has an exception. Students must register with an
@bumail.net address — that is how the system knows they are who they say they
are. But alumni no longer have one, and the first version locked them out of
their own department's events. Alumni are now exempt from the domain check
while every other validation still applies.
Every message is bilingual. Errors come back in Thai and English together, in one string, because a registration form that fails in a language you are less comfortable reading is a form you abandon.
Check-in runs inside a token window. A QR code that works forever is a QR code that gets screenshotted and forwarded. Tying validity to a window means a scan proves someone is at the door now.
Dates are normalised to Bangkok time explicitly. Apps Script will happily
hand you a Date in whatever timezone the script property says, and an event on
the 14th quietly becomes the 13th for anyone reading the export. It is a
one-line conversion and it is the kind of bug that is invisible until it is
embarrassing.
What it replaced
A printed name list at a door. Queues formed while staff searched pages by hand, walk-ups had nowhere to go, and the final attendance number was transcribed from paper the following day — by which point the paper had usually been folded into someone's bag.
Why Apps Script
Every one of these systems had to be free to run, had to live where the committee already kept its data, and had to be maintainable by a student with no server to administer and no budget to ask for. Google Apps Script met all three. All four have run at zero monthly cost since they shipped.
The constraint that shaped everything: whoever inherits these after I graduate should be able to open the spreadsheet and understand what they are looking at. That ruled out a lot of clever designs in favour of boring, readable ones.
What building on a low-code platform actually teaches
Low-code is not a lesser category of engineering. The constraints are just different ones — quota limits instead of memory limits, sheet reads instead of queries, and a column order you cannot casually change because half the system addresses columns by index.
Three habits came out of it that carried straight into the portal rebuild:
Ship on Tuesday, watch on Wednesday. The fastest way to learn what a form actually needs is to give people the version you have and see which fields they leave blank. Every one of these systems is on its second or third shape.
Measure before optimising. The Sheets call I removed from the Campus Tour dashboard was not the one I would have guessed was expensive.
Design so the schema does not break its own dependents. A column inserted in the wrong place breaks things that look unrelated, in ways that surface a week later. Getting that wrong once teaches it permanently.
Where they stopped scaling
Not at load, and not at complexity within any one system. They stopped at the seams between them.
A committee member existed three times over — once in the registry, once in the inventory system's borrow records, once in an event's participant list — under three slightly different spellings. Reconciling those had quietly become somebody's weekly job. No amount of improvement inside a single Apps Script project fixes that, which is what the portal rebuild was for.