About SpinSight
Inspiration
I created SpinSight because I was tired of making the trip to the laundry room, only to find that it was at maximum capacity. This not only wasted my time, but also meant that someone else's clean laundry might end up piled on top of the machine by an impatient student who had been waiting for one to open up.

At Webb, the washers and dryers are run by a company called Greenwald, and they publish a live status feed in their API. I figured that I could turn that status feed into a useful and actionable dashboard that students could check on before heading to the laundry room.
What I learned
While building the backend, I realized that API polls can be costly to the Greenwald server, so I had to find an acceptable tradeoff between polling duration and data accuracy. Since each washer cycle takes 37 minutes and each dryer cycle is 45 minutes long, I figured that polling every thirty minutes using a Workers Cron Trigger was enough for this project, as we can precisely extrapolate the data based on the fixed cycle durations. I also added a manual refresh button that is gated by a Cloudflare Turnstile token to prevent abuse. The backend counts attempts per visitor, so anyone who manually refreshes more than three times in a minute has to pass a stronger verification before the next one goes through. Turnstile also verifies the client upon first load, and if the client possesses bot-like characteristics, an interactive challenge is presented upon every manual refresh. I chose Turnstile because it is user-friendly and has both invisible and interactive modes, which makes it suitable for my project.
Hosting the project was also one of the dilemmas I had to face. I thought of using services like Vercel and Railway, but quickly realized that they might exceed my budget and did not suit my needs. I also considered using an old computer back in my home, but since that is located all the way across the world, port-forwarding and reliability would have been a serious issue. Finally, I settled on Cloudflare Workers because of its generous free plan and integrated production environment supporting bindings like SQL databases and CAPTCHA services. This enabled my project to be completely serverless and eliminated the hassle of managing the hosting and hardware by myself. I configured the Workers to monitor my GitHub repository, triggering a build and deploy on every push.
How I built it
SpinSight is built using three Cloudflare Workers: a frontend, a backend, and a Telegram bot that allows users to report Machine IDs and send feedback.
The frontend uses Svelte 5 and Vite. It gets compiled into static assets and is served by Cloudflare Workers. It connects with the backend and presents the data in an easy-to-understand dashboard, complete with large stats, graphs, and charts.
The backend is a small Worker that runs on a Cron Trigger every 30 minutes. It is responsible for fetching the Greenwald data, validating its shape, dynamically retrying the poll in case of errors while also respecting the Greenwald servers, and writing the data to the Cloudflare D1 database atomically. Snapshots of the laundry rooms are keyed by the machine's Bluetooth address and the poll timestamp, which makes it easy for the frontend search the data with a single query of the database.
The third Worker is the Telegram relay. It is invoked whenever a user attempts to report a problem via the footer of the page or submit a machine ID mapping. To prevent abuse, it is always gated behind a Turnstile challenge. Once the relay is invoked, it forwards the report via a Telegram bot.
All secrets and credentials are locked away in the Workers' bindings and never reach the client. They stay encrypted in the server and can always be rotated in case of a leak.
A more technical write-up is available in the repository README.
Challenges
The most annoying challenge I faced was mapping machines to specific dorms so students could view their dorm stats at a glance. I couldn't visit girls' dorms myself, so I had to ask students who lived there to report the machine IDs from their laundry rooms. That was not going to scale, so I built the Telegram relay so I could crowdsource the information.
At first, the frontend and backend lived on different subdomains (spinsight.xyz and api.spinsight.xyz). The browser blocked cross-origin requests to the API, disallowing the frontend from receiving data, so I moved the API onto the same domain under /api/. That solved the CORS issue, but Firefox on iOS still couldn't connect to the backend. It turned out that while Cloudflare did provide an HTTPS certificate for my domain, HTTP-to-HTTPS redirects were not configured. As a safety measure, Firefox prevented the API from being contacted. Once I enabled the redirect, SpinSight began working properly in Firefox.
AI disclaimer
AI was used in this project only as a programming tool. All brainstorming, ideas, design decisions, and everything else were my own, and the project reflects my own vision and work.
Built With
- api
- bun
- cloudflare
- cloudflare-d1
- cloudflare-turnstile
- cloudflare-workers
- greenwald
- svelte5
- telegram
- typescript
- vite
- wrangler
Log in or sign up for Devpost to join the conversation.