An artist site that had to be worth visiting before the album existed.
Kobe Still is a singer-songwriter with a debut album on the way and, at launch, almost nothing anyone could listen to. We designed and built the record’s home, six playable rooms made out of the songs, a live listening room and the cookie-free analytics behind all of it — as one system.
Fourteen song pages, six rooms, one live room — every route written out as real HTML before a line of JavaScript runs.

Sector
Music — artist site
Album
I Believe In Music
Build route
Bespoke website system
Live since
2026
Four constraints shaped every decision.
Most artist websites are a bio, a photo and a row of streaming icons — a page that exists so a link has somewhere to point. This one had a harder job, because at launch there was barely any music to send anyone to.
The album was not out yet
A new artist site has the hardest version of the empty-shelf problem: almost nothing to hear, and one chance with anyone who arrives. It had to be worth the visit before the record could carry it.
Artist sites rot in public
A news feed with nothing in it since last spring says more about a project than any bio. The brief ruled out anything that visibly goes stale — which meant designing the site to be finished, not fed.
No cookie banner to write
Kobe wanted to know what was landing without following anyone to find out. That is a data-layer decision, not a consent-popup decision, so it was settled before the first screen was designed.
It had to cost nothing to run
An unsigned artist should not be paying a platform fee to publish their own record. Every piece was chosen to sit inside a free tier and stay there.
Three things a stranger can do on their first visit.
Hear the record, play with something built out of it, or decide to stick around. Pick one to see the screen that carries it.
Fourteen songs, each on its own page
The album and a page per track — every one prerendered to real HTML, so a song is readable and shareable before a line of JavaScript runs. The player draws a real waveform of the file it is playing, not a decorative bar.
- Song pages, the sitemap and the social cards are all generated from one track file — nothing is maintained twice
- The site knows what may be played and from when, so an early listen can open before release day without anything being published by hand
- Lyrics are gated the same way: a flag per track, one line to flip when a song is actually out
- Each song carries its own prose rather than a stock bio paragraph repeated fourteen times

The answer to having nothing to show yet.
A pre-release artist site usually asks a stranger to care on trust. These six do the opposite: each one takes an idea from a song and turns it into something you do in under a minute — no sign-up, nothing saved, nothing to unlock. They are the reason the site was worth sharing before the album was worth streaming.
Name an artist you love. Get a song back.
Not an algorithm and not a chart — a recommendation drawn from Kobe’s own playlists. Nearly four thousand tracks, indexed at build time, so the pick is a real answer from a real record collection.
- 3,997 tracks in the library, generated from the playlists rather than typed out
- The whole index ships with the site, so a pick costs one function call and no third-party API
- Artwork and popularity are refreshed from Spotify by a build script, not fetched live
- Answers a question every artist gets asked and almost none of them answer

One stream, one clock, everybody on the same second.
A room you walk into and hear exactly what everyone else is hearing. Nobody is told where the stream is: the page asks once, measures how far its own clock is out, and works out the rest itself. That means it costs nothing while people are listening and survives a dropped connection without a reconnect handshake.

Built, not yet open
The room works. It is deliberately unlisted, kept out of search and the sitemap, and its chat is switched off at the Worker until there is enough released music to make walking into it worthwhile.
Opening it is one call to the Worker, not a deploy — and closing it takes effect instantly for everyone connected. That switch is the point of building the chat as its own service rather than part of the site.
A public chat room is a liability unless you design it as one.
Most of the live room’s build went into what happens when someone arrives to ruin it. The guards below run before a message reaches anyone else.
Slurs, sexual content, suicide taunts
Blocked, and earns a strike
Three strikes
Muted for fifteen minutes
Links of any kind
Blocked — the main spam and phishing route
Message rate
One per 1.5s, five per 20s, forty per five minutes
Duplicate message
Dropped silently within a minute
Room flooding
Slow mode above 25 messages per 10 seconds
Shouting and key-mashing
Blocked as noise
Connections per IP
Capped at three
Message storage
None — last 80 in memory, gone when the room empties
Evasion is handled before matching. Accents are folded, digit substitutions mapped, repeated letters collapsed, and zero-width and direction-flipping characters stripped. Runs of single letters are joined back together, so a slur spaced out one character at a time collapses into itself — without swallowing the ordinary words that happen to contain a banned string inside them, which is the test most naive filters fail.
GIFs are proxied and signed. The search key never reaches a browser, the safe-content rating cannot be stripped from a query, and every image URL the picker hands out carries a signature the room checks — so a URL found somewhere else is refused rather than embedded. Results are cached at the edge, so a room full of people searching the same word costs a single call.
None of this is airtight against someone determined. It makes the room boring for drive-by abuse and gives the artist a switch for when it is not.
If you ask people about their lives, you own what comes back.
This site asks real questions. Song pages end on one aimed at the reader, and several of the rooms invite someone to write down something they have never said out loud. Build that and you will eventually catch a message from a person having the worst night of their life — so it was designed for on purpose rather than discovered later.
It is never posted, and never stored
A message that reads as distress does not reach the board, the chat or the database. It is answered privately, on the spot, with a support card carrying real helpline numbers — and it never counts as a violation or earns a strike.
One list, imported by both sides
The live room checks it on the server; the browser-only rooms check it in the page. Both import the same module rather than keeping a copy each — because the copy that quietly stops matching looks exactly like the one that still works.
It deliberately catches too much
The threshold is set low on purpose. A false positive costs someone a mild moment of oddness; a miss costs someone the one screen that had the number on it. That trade was made once, in writing, and the code follows it.
Nothing about the person is kept either. No email, no account, no IP address. The only identifier stored alongside a message is a salted one-way hash, kept purely so a single visitor cannot flood a page — and no endpoint on the site will ever hand it back.
Analytics with nothing to consent to.
The site knows which pages are landing and which songs get finished. It does it without cookies, without a third-party tracker and without storing an IP address — so there is no consent banner, because there is nothing to consent to.
Visits
Path, the referring host only, country, and a per-tab session. Pruned to 90 days.
Plays
Start, qualified at thirty seconds, complete at ninety per cent — per song, per session.
Comments and subscribers
What was left, and the country it came from. Nothing that identifies a person.
Never
No cookies, no third-party tracker, no IP address stored — which is why there is no cookie notice to write.
A session is a tab, not a person.
The identifier lives in the tab and is forgotten the moment it closes. So “visitors” honestly means distinct tabs — less flattering than the number a tracker would report, and enough to compare one page against another.
Country comes from the edge as a two-letter code and is named on the page by the browser itself, so no lookup table ships and no country name goes stale. It is read as a direction rather than a fact — a VPN reports wherever it exits.

The privacy page is written from the code: every promise on it names the endpoint behind it, so changing what the site stores and changing the page are the same job.
Counting it is only half a feature. This is the other half.
The tracking was written before there was anywhere to read it, which meant the only way to see any of it was raw SQL. So the build includes the page that answers the questions instead: which pages land, whether a song holds, who walked into the room, and what needs taking down. One token guards all of it, checked on the server on every request — and if that secret is ever missing the endpoint refuses everything rather than defaulting open.
Which pages land, and whether it is the same person twice
Views counts page opens; visitors counts distinct tabs. The gap between the two columns is the useful part — a wide one means somebody is going back and forth rather than a crowd arriving.
- Every room is listed as its own page, so it is obvious which ones people actually open
- Sources are recorded as a host and nothing more — and the page says outright that a large "direct / unknown" is normal, because most social apps strip the referrer
- Countries are sorted by listeners rather than visitors: a country that plays the songs is worth more than one that opened a page
- The honest edge cases are shown rather than hidden — unrecorded countries get a row too

A note on these screenshots. We replaced the real data with fake data for data-protection reasons. Every figure, every name and every message shown on these admin screens is invented — none of it is real traffic, real listening or anything a real visitor wrote. The live-room table shows what that screen looks like with a room running; the room has not opened yet. The interface, layout and workflow are exactly as delivered; only the data is fictional, and no real figure from this page appears anywhere in this study.
Dark, warm and almost entirely typographic.
There is no press shot carrying this site. It is set on a near-black brown field in a cream ink, with a single gold accent reserved for the things you can act on — so the words and the song titles do the work that photography usually does on an artist page. Corners are almost square: a 4px radius throughout, which reads as print rather than app.
Ink
#FBF8E2
Gold
#E2B873
Gold soft
#F4E7B2
Brown
#693C02
Panel
#1A120C
Base
#0B0705
Typography
Anton for the shouting, Barlow for the talking
A condensed display face in uppercase for headings, a plain workhorse for body copy, and a small-caps cut at wide tracking for every label and nav item. Three files, self-hosted, so nothing reflows halfway through loading — and body text is held to a 38rem measure so the prose under each song stays readable.

Written once, generated everywhere.
The songs are the source. Nearly everything else on the site — pages, sitemap, social cards, the audio itself — is produced from them by a build step, so there is no second copy to forget to update.
Write
The fourteen tracks and the prose under them live in one place. Song pages, the sitemap and the social cards are all generated from it.
Build
Vite builds twice — once for the browser, once for the server — then a prerender step writes real HTML for every route on the site.
Serve
Cloudflare Pages serves that HTML as static files. Only the handful of things that genuinely need a server run as functions.
Count
Visits and plays are written to the project’s own database, read on a private page, and pruned after ninety days.
Release day is a one-line change. Lyrics are held back until a song is actually out, controlled by a single flag per track. Flip it, rebuild, and the words appear on the song page, in the markup search engines read, and nowhere else — no page to write, no copy to paste, no risk of publishing a lyric for something nobody can hear yet.
Music gets shared as a link, opened on a phone.
Every route — including the game — was drawn at phone width first, because that is how a song reaches someone who has never heard of the artist.

Home

A song

The rooms
Built to run on almost nothing.
An independent artist should not be paying a monthly platform fee to publish their own record, or renting their mailing list back from the company that stores it.
Vite + React, prerendered to static HTML
Every route is written out as a real HTML file at build time. A song page is readable, indexable and shareable before any JavaScript runs.
Cloudflare Pages + Pages Functions
Static files from the edge, with a small number of server functions for the things that cannot be static — the recommendation, the mailing list, the board.
D1 for the list, the board and the counts
Subscribers, comments, visits and plays live in the project’s own database. The mailing list is an asset Kobe owns, not one rented from a platform.
A separate Worker for the live room
The room’s chat runs on its own Worker with a Durable Object, so the rest of the site cannot be affected by it — and it can be switched off without a deploy.
Self-hosted fonts and encoded audio
Three faces served from the site itself, so headlines never reflow mid-load. Audio is encoded from the masters by a build script; the masters never reach the site.
Static-first, phone-first
The design was drawn at phone width and the pages work with JavaScript switched off, because both are how people actually arrive from a link.
The strongest choices on this site are the things that are not on it.
Every one of these was proposed, considered and cut. An unmaintained page is worse than no page: a blog with three posts from last year, a news feed frozen mid-campaign or a shop with nothing in it all tell a visitor the same thing about a project, and none of it is true.
So the site was designed to be finished rather than fed. The home page is the canonical link-in-bio, because a second page doing that job is a second page to maintain. It is the same discipline behind the rooms: things that are complete, not things that need topping up.
Deliberately absent
- No blog
- No news feed
- No “latest” section
- No link-in-bio page
- No shop
- No cookie banner
A record label’s infrastructure, without a record label.
This is what the build put in Kobe’s hands:
A visitor who has never heard the music has six things to do on their first visit
Every song page is real HTML — indexable, shareable, readable before the JavaScript loads
Lyrics go live per song with a one-line change on release day
The mailing list writes to a database the artist owns and can export
Kobe can see what is landing without a cookie banner or a third-party tracker
The whole site runs inside free tiers — no monthly platform fee to publish a record
Want a site that does more than sit there?
Answer a few questions about what your website actually has to do and get a custom preview — no commitment, and clear pricing before you start.

