63 generators. Numbers, words, equations, patterns, images, sound. Every one seeded from the physical world. Every result ships with a receipt.
1,286 tests · 10 entropy sources · 0 API keys · no database
Most random tools call rand() and move on. Randomly makes the source of the
randomness the product:
20, 22, 26, 35, 47, 51 A magnitude 3.6 earthquake, 67 km N of Culebra, Puerto Rico, 39.6 km down — 52 minutes ago. ↗
That link goes to the USGS. You can check it. Every result on the site works this way, and every result is reproducible from its own URL — with no database behind it, because the token is the seed.
Two of them are arguments rather than decoration:
And numbers.coordinates renders the classic sphere-sampling bug side by side.
Above 60° of latitude lies 13.4% of the Earth. The correct sampler put 14.0%
of its points there; sampling latitude uniformly put 35.9% — because it gives
a coin-sized polar cap the same number of points as a belt around the equator.
And the things that are not pictures:
words.passphrase poster-haggler-spoils-bobbing-scrap-morbidity
77.5 bits · capitalisation and separators add 0.00 bits,
because the rule is deterministic
words.syllabic Mirceeri · Thrinhailgleer · Girnailler · Nethyaebrin
equations.quadratic -3x² + 3x + 126 = 0 → x = -6 or x = 7
equations.calculus ∫ cos(x) - x·sin(x) dx → x·cos(x) + C
equations.identity cos(2x) = 2cos(x)² + 1 → False — a − became a +
numbers.dice 4d6kh3 → 12, 12, 14, 13, 11, 13
Ten sources. All free, all keyless, all credited — and classified honestly.
| Source | What it actually is | Class | |
|---|---|---|---|
| ⚛ | ANU Quantum Vacuum | vacuum field fluctuations, homodyne detection in Canberra | A |
| 📡 | RANDOM.ORG | atmospheric radio noise, largely lightning, sampled in Dublin | A |
| 🔒 | OS CSPRNG | the kernel entropy pool of the machine serving the page | A |
| 🏛 | NIST Beacon | 512 bits published every minute, hash-chained and signed | B |
| 🌍 | drand | threshold BLS signature, League of Entropy | B |
| ⛏ | Bitcoin | the block hash the whole network spent ten minutes finding | B |
| 🌋 | USGS Seismic | every earthquake on Earth in the past hour | C |
| ☀ | NOAA Space Weather | the planetary K-index | C |
| 🌡 | Live Weather | temperature and wind at a randomly chosen city | C |
| 🛰 | ISS Position | the station's ground track, 408 km up | C |
Class A stands alone. Class B is unpredictable until published, then public forever. Class C is flavour — an earthquake carries maybe thirty bits of genuine surprise, not the twelve kilobytes its JSON weighs.
So Class B and C are always mixed with fresh CSPRNG bytes before anything is generated, and the receipt says so. The site credits the earthquake for the story and guarantees the cryptography separately. Both facts are true at once, and both are on the page.
"receipt": {
"source": "seismic",
"class": "C",
"caveat": "Carries only tens of bits of genuine surprise. Always mixed with the OS CSPRNG.",
"narrative": "A magnitude 3.6 earthquake, 67 km N of Culebra, Puerto Rico, 39.6 km down — 52 minutes ago.",
"proof_url": "https://earthquake.usgs.gov/earthquakes/eventpage/us7000th33",
"mixed_with_csprng": true,
"degraded": false
}A generator that consumes no randomness at all — an identicon is a pure function of the text you type — skips the entropy draw entirely rather than crediting a beacon that changed nothing about its output. A false receipt is the one thing this project will not ship.
One contract. A generator declares a ParamSchema, and the studio's control
panel, the API's validation, the catalogue entry and the permalink format all render
from that declaration. Adding a generator is one class plus one config line — no
route, no controller, no view.
HKDF all the way down. Raw source material is conditioned into a 16-byte seed with HKDF-SHA256, mixed with fresh local entropy so a cached NIST pulse can never yield a repeated seed. That seed expands into an endless deterministic byte stream.
The token is the seed. 16 bytes encode to 26 Crockford base32 characters, so a permalink recomputes rather than looks up. No database, no storage, no accounts.
Specs, not pixels. Canvas and audio generators send a description — algorithm, parameters, palette, a render key — and the browser re-derives the identical byte stream and does the drawing. A four-minute generative piece is a 2 KB response, and dragging a slider re-renders instantly without touching the server.
That last one is a real correctness claim, so it is tested: RngParityTest runs the
PHP and JavaScript implementations side by side and compares raw stream bytes,
integer draws with their rejection counts, doubles, shuffles and token decoding.
isSensitive() |
A password gets no shareable seed. A secret with a replayable URL is not a secret. |
isReproducible() |
A generator built on live external data gets no permalink — that link would resolve differently tomorrow. |
usesEntropy() |
A deterministic generator skips the entropy draw, instead of spending 850 ms on a beacon it will not use. |
And shipped versus planned generators live in disjoint lists, with a test that fails if one appears in both — so the site can never advertise a generator it has not written.
No key. No signup. No account. Rate limited to 60/minute.
curl -s 'https://randomly.test/api/v1/g/numbers.integers?count=6&min=1&max=60&unique=1' \
-H 'Accept: text/plain'
# 20, 22, 26, 35, 47, 51GET /api/v1/generators the whole catalogue, machine-readable
GET /api/v1/sources live status of all ten entropy sources
POST /api/v1/generate {generator, params, source}
GET /api/v1/g/{key}?… the one-liner shorthand
GET /api/v1/replay/{token} recompute a past result exactly
Every generator is fully usable over HTTP. A canvas generator returns a spec by design — that is what keeps the studio's sliders instant — but an API caller handed a description of an image has not been given an image:
curl -o noise.png '…/api/v1/g/patterns.perlin?variant=ridged&palette=viridis&format=png'
curl -o beat.wav '…/api/v1/g/audio.rhythm?format=wav'Those are rendered by running the same JavaScript the browser runs, under Node.
A PHP reimplementation would have been a second copy of a generative algorithm to
keep in step, and it would have drifted silently. Asking for a format a generator
cannot produce returns 406 naming the ones it can — never a quiet JSON fallback.
The site's own pages call these same endpoints. There is no private path with different behaviour.
Rate limits are per IP and in two buckets: 60 requests a minute, of which 20 may be
image or audio renders. A render starts a Node process and costs orders of magnitude
more than a JSON response, so charging both against one allowance would mean either
throttling cheap calls needlessly or letting sixty renders saturate a box. A 429
returns retry_after_seconds and both limits, because the caller is usually a script.
The repo ships a Claude Code skill describing the API — the generators, the entropy classes and what each is safe for, the reproducibility rules, and the error codes. Drop the repo into a project and an agent can call the API correctly without being told how.
Rendered server-side with GD, recomputed from the token, cached by content hash. The receipt sentence travels in the URL — signed, because letting a query parameter put arbitrary text on a card bearing the wordmark would be building a forgery generator.
composer install && npm install
cp .env.example .env && php artisan key:generate
npm run build
php artisan servephp artisan randomly:seed --source=drand # draw one seed, see its receipt
./vendor/bin/pest # 1,286 tests, no network, no databaseStack: Laravel 13 · PHP 8.5 · Blade + Alpine 3 + Tailwind 4 · Vite · Pest.
Requires gd for Open Graph cards and Node for the verification scripts.
Three classes of defect here are invisible to a test suite, so each gets a harness that measures the real output:
| Script | Measures | Caught |
|---|---|---|
render-preview.mjs |
renders canvases to PNG under Node, no browser | mud-coloured palettes; a Float32Array rounding bug indexing colours[-1] |
analyse-audio.mjs |
spectral slope, pitch by autocorrelation, decay, peak | noise colours an octave-slope out; a Karplus–Strong filter running backwards |
check-latex.mjs |
parses generated LaTeX through real KaTeX | e^{a}^{2} — valid PHP output, invalid TeX |
Every one of those was found by looking at the output, not by a failing assertion. The Gray–Scott labyrinth constant in the spec was wrong too, and rendering both values side by side is the only reason anybody noticed.
The whole project was specced before it was built. docs/ carries
the architecture, every module's algorithms and equations, the entropy conditioning,
the API contract, the brand direction and the build order — updated as
implementation contradicted it, with the contradictions written down.
| Overview · Architecture · Entropy | positioning, the contract, the sources |
| Numbers · Words · Equations | unbiased sampling, Diceware, backward construction |
| Patterns · Images · Audio | noise, colour, synthesis |
| UI & Brand · API · Roadmap | presentation, endpoints, what's next |
Randomly stands on work it did not pay for and could not have afforded: RANDOM.ORG, the Australian National University, NIST, the League of Entropy, mempool.space, the USGS, NOAA, Open-Meteo, Open Notify, Wikipedia, GBIF, and the EFF's Diceware wordlists (CC BY 3.0 US). Not one of them charges. Not one asked for a key. Every one of them could have.
Free does not mean uncredited. The full list lives at /credits.
Every generator the specification described is written and running.
The catalogue never counted unwritten code, which is why the number took a while to get here.












