Case study

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.

kobestill.com
Kobe Still homepage, scrolling

Sector

Music — artist site

Album

I Believe In Music

Build route

Bespoke website system

Live since

2026

The brief

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.

01

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.

02

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.

03

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.

04

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.

The site

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
kobestill.com/music/i-trust-it
Fourteen songs, each on its own page — desktop
The rooms

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
kobestill.com/rooms/one-song
Name an artist you love. Get a song back. — desktop
The live room

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.

kobestill.com/rooms/live
Kobe Still live listening room

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.

Duty of care

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.

What it counts

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.

kobestill.com/privacy
Kobe Still privacy page

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.

Behind the token

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
kobestill.com/admin
Which pages land, and whether it is the same person twice — desktop

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.

The look

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.

kobestill.com/about
Kobe Still about page
How it is built

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.

1

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.

2

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.

3

Serve

Cloudflare Pages serves that HTML as static files. Only the handful of things that genuinely need a server run as functions.

4

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.

Mobile first, properly

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 on mobile

Home

A song on mobile

A song

The rooms on mobile

The rooms

Under the bonnet

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 decisions you cannot see

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
What the artist can now do

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.