biehl.io
All case studies

KFUT 89.5 FM / Tuolumne County community radio

Building the Digital Home for KFUT Community Radio

I designed and built KFUT Community Radio’s digital home, connecting the live broadcast, RadioDJ schedule automation, local events, artist music uploads, station publishing, audience relationships, and support workflows in one responsive platform.

Who
KFUT 89.5 FM / Tuolumne County community radio
What
Design and build a community radio platform across broadcast, content, participation, and station operations
Result
A live platform connecting streaming, schedules, events, artist submissions, publishing, fundraising, and community participation
KFUT Community Radio full-stack design engineering case study cover

Project snapshot

Client / context: KFUT 89.5 FM Community Radio, serving Twain Harte, Sonora, and the surrounding Tuolumne County region

Role: Full-stack design engineer

Focus: Community media UX, live radio, schedule automation, participatory publishing, artist submissions, station operations

Stack: Next.js App Router, React, TypeScript, PocketBase, Tailwind CSS, shadcn/ui, RadioJar, ACRCloud, RadioDJ / MariaDB, Resend, OpenAI API, Google Maps, OpenWeather, Coolify

Live site: kfut.org

One-line version

I designed and built KFUT’s digital home: a community-radio platform that connects the live broadcast, weekly programming, local events, music submissions, station publishing, fundraising, and audience participation in one responsive experience.

The challenge

A community radio station is more than an audio stream. It is a local information service, a cultural archive, a volunteer organization, a platform for independent artists, and a relationship between broadcasters and listeners.

KFUT needed a website that could represent all of those roles without feeling like a generic station template. The public experience had to make the station immediately useful:

  • listen to the live broadcast
  • understand what is playing
  • see the weekly schedule
  • find local events and station news
  • learn about the team and mission
  • support the station through donations and underwriting
  • volunteer
  • submit original music for airplay
  • subscribe for updates

Behind those public surfaces, the station also needed operational tools. Schedule information originated in RadioDJ. Events and news required review before publication. Artists needed verified accounts and reliable file uploads. Newsletter contacts had to stay synchronized. Admins needed to manage content without editing the application.

The product challenge was to turn a small station’s collection of broadcast systems, community workflows, and third-party services into one coherent platform that volunteers could operate and listeners could trust.

My role

I worked as KFUT’s design engineer and built the platform end to end.

My work included:

  • defining the site architecture around listening, programming, participation, and support
  • designing the responsive visual system and reusable component library
  • building the Next.js application and PocketBase content model
  • integrating the RadioJar live stream and now-playing metadata
  • adding ACRCloud recognition and stale-result handling for more reliable track details
  • designing desktop and mobile weekly schedule experiences
  • automating RadioDJ schedule imports from MariaDB into PocketBase
  • building authentication, email verification, password recovery, and role-based access
  • creating moderated event and news publishing workflows
  • adding AI-assisted event extraction from posters and event URLs
  • building a verified artist music-upload workflow with shared client/server validation
  • adding confirmation emails and operational upload auditing
  • synchronizing newsletter subscribers between PocketBase and Resend
  • integrating donation, underwriting, volunteer, weather, contact, and supporter experiences
  • evolving and hardening the product through continuous production use

Selected work

1. Designing the station around listener intent

I organized the site around the jobs listeners and community members actually arrive to do.

The primary experience connects:

  • the live station
  • the current program and track
  • the weekly schedule
  • upcoming events
  • the station’s mission and people
  • ways to participate
  • ways to support the station

The home page combines the station story with local weather, volunteer recruitment, staff, underwriters, supporters, newsletter signup, and FCC information. The navigation keeps high-frequency destinations such as Schedule and Events visible while giving donation, contact, login, and music upload their own clear paths.

This was an information-architecture decision as much as a visual one. KFUT’s value is not contained in one feature. The site needed to show how broadcasting, local information, artists, volunteers, businesses, and listeners reinforce one another.

2. Making live radio feel dependable

The radio player is a persistent product surface, not a decorative embed.

I built a custom player around the RadioJar stream with:

  • explicit play and stop states
  • buffering feedback
  • volume and mute controls
  • responsive minimized and expanded layouts
  • retry-aware metadata polling
  • visibility-aware updates
  • failure handling that keeps controls understandable
  • station and track information that can update without interrupting playback

Now-playing data is inherently imperfect because the stream, automation system, and recognition provider do not always change at the same moment. I designed a layered approach rather than trusting one response.

RadioJar provides the stream’s track metadata. ACRCloud can improve title, artist, album, artwork, and music-service links. The application tracks recognition timestamps, rejects stale ACRCloud results, detects track changes, and falls back to RadioJar when recognition has not caught up.

This was a small but important design-engineering problem: the interface should not confidently show the previous song just because an API returned a technically valid response. Freshness became part of the product contract.

3. Turning broadcast automation into a public schedule

The station’s programming schedule already existed in RadioDJ, but its data was designed for broadcast automation rather than public presentation.

I built a synchronization pipeline that reads enabled RadioDJ events from MariaDB, interprets RadioDJ commands, classifies real programs, excludes generic rotations and filler content, and writes managed shows and schedule slots into PocketBase.

The sync handles several broadcast-specific edge cases:

  • programs loaded from RadioDJ categories
  • shows loaded by individual track IDs
  • stream-backed programs whose generic source labels are not useful to listeners
  • day codes embedded in either structured fields or event names
  • adjacent schedule events that belong to one continuous program
  • programs that cross midnight
  • manual PocketBase records that should not be overwritten
  • existing show descriptions and images that should survive later syncs

I separated synced records from manually managed records with stable source keys. That lets automation update the schedule without taking ownership of editorial content that station staff maintain in PocketBase.

On the public side, I designed two schedule experiences:

  • a desktop time grid with seven day columns, time alignment, live badges, and richer hover details
  • mobile day tabs with readable cards, artwork, descriptions, and time ranges

The result turns operational broadcast data into a schedule listeners can actually scan.

4. Building community events as a moderated contribution flow

Local events are central to community radio, but accepting public submissions creates a trust and workflow problem.

I designed the event system around contribution with moderation:

  1. a community member creates an account
  2. email verification establishes a basic trust boundary
  3. the member submits event details and a cover image
  4. the event enters a pending state
  5. an admin reviews and publishes it
  6. only published, upcoming events appear publicly

The public events page prioritizes the next upcoming event, then presents the remaining calendar in a responsive grid. Event detail pages preserve location, dates, descriptions, imagery, and external links.

For station admins, I also built an AI-assisted intake tool. An admin can provide an event poster, an event URL, or both. The server reads useful page metadata and visible text, submits structured context and imagery to the OpenAI API, validates the response against a strict schema, normalizes dates, and fills the event form while leaving uncertain required fields visible for human review.

The goal was not automatic publishing. It was reducing repetitive transcription while keeping the admin in control of the final record.

5. Creating a direct path for independent artists

KFUT’s mission includes music and voices that listeners may not find elsewhere. The website needed to turn that value into a real contribution path.

I built a music-upload workflow for artists with:

  • account and email-verification requirements
  • clear MP3, file-size, and submission-count rules
  • drag-and-drop multi-file upload
  • artist and optional album metadata
  • radio-edit disclosure
  • copyright confirmation
  • shared validation rules across client and server
  • persistence checks that confirm every attempted file was saved
  • confirmation emails listing the accepted tracks
  • upload counts surfaced on the site
  • an audit script for diagnosing incomplete or malformed submissions

The important product decision was to treat successful upload as more than “the request returned.” The server compares the number of files attempted with the number actually persisted. If the counts differ, the artist receives an explicit incomplete-upload result instead of false reassurance.

That boundary matters for community trust. An artist should know whether the station received the work they submitted.

6. Giving station staff a manageable publishing system

PocketBase acts as the application’s content and identity layer for users, events, news, songs, shows, schedule slots, and related records.

I built server actions for the system’s mutations and paired them with Zod schemas so the UI and backend agree about valid data. Cookie-based sessions support login, signup, verification, password reset, and protected routes. Middleware separates public pages, verified-member actions, and admin-only publishing surfaces.

Admins can review pending events and news, publish or unpublish records, and let the public pages revalidate without a code deployment.

This kept the operational model appropriately small. KFUT did not need a heavyweight enterprise CMS; it needed clear permissions, simple review states, file handling, and an interface that volunteers could understand.

7. Connecting audience relationships across services

The website includes several different forms of participation:

  • volunteer interest
  • newsletter subscription
  • contact messages
  • artist uploads
  • event submission
  • underwriting
  • donations

Each path has a different backend destination, but they need a consistent public experience.

I integrated:

  • Resend for transactional messages and newsletter contacts
  • a scheduled PocketBase-to-Resend subscriber sync
  • unsubscribe and resubscribe handling
  • duplicate-contact prevention
  • rate-aware synchronization
  • contact-form email delivery
  • Zeffy donation and underwriting entry points
  • supporter showcases
  • volunteer calls to action
  • local weather for communities in the broadcast region

The newsletter sync is intentionally stateful. PocketBase records whether a verified subscriber has been synchronized, and the scheduled job updates only what needs attention while preserving unsubscribe state.

These connections turn the site from a brochure into relationship infrastructure for the station.

8. Building a visual system with local character

The interface needed to feel specific to KFUT while remaining practical for ongoing content.

I used the station’s colors, logo, mascot, imagery, and display type to create a recognizable identity, then grounded it in reusable Tailwind and shadcn/ui components for forms, cards, dialogs, sheets, tabs, alerts, badges, carousels, and controls.

Responsive behavior was designed into the core surfaces:

  • a mobile navigation sheet instead of compressed desktop links
  • schedule tabs instead of an unreadable seven-column grid
  • adaptive radio-player layouts
  • stacked event and supporter cards
  • accessible labels and focus states
  • clear loading, empty, success, and error states

The design system gives the station personality without making future publishing dependent on custom page design.

Design engineering approach

Treat the broadcast as a system, not an embed

A radio website can appear to be a player plus content pages. In practice, the listener experience depends on stream behavior, metadata freshness, automation schedules, editorial descriptions, and responsive UI. I designed those layers to work together.

Keep humans in control of editorial decisions

Automation imports schedule structure. AI helps transcribe event information. Neither decides what the station publishes. Manual content, admin review, and source-specific ownership remain explicit.

Make participation safe and legible

Public contribution only works when the system communicates expectations. Verification, moderation, upload requirements, copyright confirmation, persistence checks, confirmation messages, and recoverable errors are all part of the participation experience.

Design for a volunteer-operated organization

The right architecture was not the most elaborate one. It was a maintainable set of tools that reduced duplicate work, kept routine operations visible, and allowed station staff to update content without touching the codebase.

Impact

The project gave KFUT a production digital home at kfut.org that connects the station’s public mission with its operational systems.

It delivered:

  • a responsive live-radio experience with now-playing enrichment and safe fallbacks
  • a public weekly schedule generated from RadioDJ broadcast data
  • community event submissions with verification, moderation, and AI-assisted admin intake
  • news publishing and role-based admin controls
  • a verified artist music-upload channel with persistence validation and confirmation email
  • newsletter, contact, donation, underwriting, volunteer, weather, and supporter experiences
  • PocketBase-backed content operations that do not require a deployment for routine publishing
  • scheduled synchronization for broadcast programming and newsletter contacts
  • one coherent platform for listeners, artists, volunteers, local organizations, and station staff

The most important outcome is that KFUT’s website now behaves like part of the station. It does not only describe the community; it gives the community ways to listen, contribute, discover, support, and participate.

What made this hard

The complexity came from connecting systems with different concepts of time, identity, and ownership.

The project crosses:

  • live audio state in the browser
  • RadioJar metadata
  • ACRCloud recognition timestamps
  • RadioDJ automation events
  • MariaDB schedule data
  • PocketBase content and authentication
  • admin and community-member permissions
  • uploaded media
  • transactional and newsletter email
  • third-party donation and volunteer services
  • public content that must remain usable on mobile

Many failures were not binary. A stream could play while metadata was stale. A file request could succeed while not every file persisted. A schedule event could be valid for RadioDJ but meaningless to a listener. An AI extraction could be structurally valid while still missing a required editorial field.

The design work was defining what “correct” should mean in each of those partial states, then making that state understandable in the interface.

What I learned

Community platforms benefit from small, explicit trust boundaries.

For KFUT, those boundaries include:

  • verified before contributing
  • pending before published
  • previewed before accepted
  • synchronized without overwriting manual editorial work
  • recognized only while metadata is fresh
  • successful only when all uploaded files are confirmed

I also learned that operational automation creates its most value when it preserves human context. The schedule sync is useful because it carries timing from RadioDJ while leaving show descriptions and artwork in the hands of the station. The event extractor is useful because it reduces typing while leaving publication to an admin.

Why it matters

This case study shows full-stack design engineering in a community-media context.

I did not only design the public pages or wire up an audio stream. I connected broadcast automation, live metadata, content publishing, identity, moderation, artist intake, email operations, local information, and fundraising into one product system.

The work demonstrates the role I value most: moving between user experience, interface design, application code, backend data, integrations, and operational workflows until a complicated organization feels simple to engage with.