RandomTools.
A single-site random toolkit: a customisable spinning wheel with fair and transparent odds, duck race, draw, coin flip, dice, plus number / password / position generators — client-side, vi/en, SEO-complete.
01.Overview
The big pictureRandom-tools grew out of a very office-culture habit: every lunchtime someone would open Wheelofnames to spin for where to eat, and every evening there'd be another site to draw lots for who picks up the tab. Most of those tools are cluttered with ads, don't remember history, have no Vietnamese support, and no one really knows whether the algorithm underneath is actually fair — which is the one thing a randomisation tool absolutely has to get right.
I put the 10 most-used tools together — spinner, duck race, draw lots, number picker, coin flip, dice, random number, password, location, gift picker — into a lean Next.js site: no ads, every entry gets equal probability, history saved locally in the browser, full vi/en support and proper SEO. The first version shipped after 3 weeks of evenings. Currently serving ~1.4k spins a day.
02.Features
What it does- 01Customisable wheelCustom names · live preview · saved presets
Paste a list — separated by line breaks or commas — and the spinner redraws in real time on SVG as you type, no extra steps needed. A set of presets covers the most common situations: lunch spots, who pays, drinking night, FIFA, League of Legends — pick one and the names fill in instantly, ready to spin. An option to remove the winning name after each round ensures the next spin never repeats the same person.
- 02Fair & transparent oddsEqual odds · open-source math
Every name in the list has exactly
1 / nprobability — no more, no less. The entire random selection logic lives in around ~20 lines insidespin-utils.ts, readable directly in the source. No hidden weights, no server-side overrides, no A/B testing. Anyone who wants to verify can open DevTools and check — there's nothing to hide. - 0310 random toolsSpin · race · draw · dice · coin · …
Beyond the main spinner, the toolkit includes: duck racing, gift picker, number roller, coin flip, dice, draw lots, random number, password generator, and location picker. Each tool lives in its own
page.tsxwith dedicated metadata and JSON-LD — every page is independently indexed and searchable, not everything crammed into a single URL. - 04Full i18n + SEOVietnamese / English · per-page metadata
i18next is bootstrapped at the layout level, switching the entire interface between languages without a page reload. Each tool calls
buildPageMetadata()to generate a full metadata set: JSON-LD WebPage, canonical, OpenGraph, Twitter card — enough for clean previews across every platform. Sitemap, robots, and manifest are configured to index based on environment — open in production, blocked in staging — no manual changes needed on each deploy. - 05Local-first historylocalStorage · zero backend
Spin history and player lists are stored directly in the browser's
localStorage—spin:lucky-spin-historyfor history,spin:lucky-spin-playersfor the list. No accounts, no database, nothing leaves the user's device. Privacy by default, not privacy by policy.
03.Tech stack
Tools used| Frontend | Next.js 16 (App Router) · React 19 · TypeScript · Tailwind 4 · lucide-react |
| Randomness | crypto.getRandomValues when available · Math.random fallback · uniform sampling · no hidden weights |
| i18n | i18next · react-i18next · vi / en JSON resources · client + server bootstrap |
| SEO | next/metadata · JSON-LD WebPage · OpenGraph · Twitter card · sitemap · robots · manifest · canonical + alternates |
| State | React state · localStorage (history, player lists) · no database |
| Infra | Docker · Vercel-style deploy · port 3024 · GitHub Actions |
04.How it works
ArchitectureThe core of the spinner is pickUniformIndex(n) — just a few lines: Math.floor(rand() × n) where rand() is crypto.getRandomValues normalised to [0, 1), with a fallback to Math.random() for older environments. Every position on the wheel has a probability exactly equal to 1 / n — no hidden weights, no IP-based overrides, no A/B testing.
The order matters: the result is determined first, the animation is just how it's presented. Once pickUniformIndex returns the winning index → calculate the target angle → add MIN_SPIN_ROUNDS × 360° for the sensation of multiple rotations → CSS cubic-bezier transition over SPIN_DURATION_MS (7000ms). Because the outcome is locked in from the start, anyone can reverse-engineer the animation to verify it: measure the final angle, count the segments, and confirm the probability is genuinely 1 / n — no need to take anyone's word for it.
"I didn't add any feature before being certain the probability was correct. The spinner could look rough, presets could be missing, history might not save — people would still come back if they trusted the result was fair. Lose that trust and nothing else matters. So the most carefully written thing in the codebase isn't the animation or the i18n — it's pickUniformIndex."

TradingSignals is a personal buy/sell signal tool for crypto and equities — not a mass copy-trading bot, but a system built for one person, backtested against 2–5 years of real market data. NestJS scans simultaneously across multiple timeframes from 15 minutes to 1 month, running in parallel through 4 independent engines: candlestick pattern, indicator, price action and volume analysis. A signal is only recorded when multiple timeframes converge — cutting through noise and surfacing only the entry points genuinely worth attention. After 10 days, the system looks back at each signal — right or wrong — gradually sharpening its accuracy the longer it runs. Results are pushed directly to a personal Telegram the moment a signal fires, with patterns rendered in real time on a TradingView chart embedded in Next.js.

Hotel Management is an internal hotel administration system built for front desk staff and operations managers. The entire core workflow runs within a single system: accepting reservations from walk-in guests or online channels, handling check-in and check-out, recording ancillary services incurred during the stay, and automatically generating invoices at checkout. Permissions are scoped down to individual actions — receptionists, cashiers, and managers each have different screens and access levels. Every change in the system is written to a full audit log, with enough detail to trace back anything when needed.
BankNotify turns an old Android phone into a DIY payment gateway for small merchants. A Flutter + Kotlin app runs in the background on the device, reads incoming balance change notifications from the banking app, and forwards them to a backend via HTTP. The backend receives the notification, matches it against a pending order, and fires a callback to complete the payment — all within seconds, no formal payment gateway integration required, no monthly fees.
A NestJS API that auto-logs into ChatGPT via puppeteer-real-browser (Cloudflare anti-bot bypass), supports email OTP, persists the session for route /ask reuse and directly promt promt, and runs an hourly cron that pings Telegram when the session expires. This allows me to send prompts and receive responses directly through the API without needing to open a browser.
05.Comments
Leave a few wordsNo comments yet.