Lofi radio streams are some of the most successful 24/7 channels on YouTube. The "Lofi Girl" model - calm music over an animated loop - generates millions of views with minimal ongoing effort. Here's how to build one technically.
Audio Pipeline
Music Source
You need a library of lofi tracks. Options:
| Source | Cost | Licensing | |--------|------|-----------| | Commission artists | $50-200/track | Full rights | | Royalty-free libraries | $10-30/month | Stream license | | Creative Commons | Free | Attribution required | | AI-generated | Generation cost | Check platform license |
Plan for 4-8 hours of unique music before tracks repeat. At an average of 3 minutes per track, that's 80-160 tracks.
Audio Processing
Normalize all tracks to the same loudness and add smooth crossfades:
# Normalize audio to -14 LUFS (standard streaming loudness)
ffmpeg -i input.mp3 -af loudnorm=I=-14:TP=-1:LRA=11 output.mp3
# Create crossfaded playlist
ffmpeg -i track1.mp3 -i track2.mp3 -filter_complex "[0][1]acrossfade=d=5:c1=tri:c2=tri" output.mp3
The -14 LUFS standard prevents jarring volume changes between tracks.
Visual Component
Animated Loop
Most lofi streams use a looping animated background. Options:
| Visual Type | Effort | Result | |------------|--------|--------| | Static image | Minimal | Boring but functional | | Animated GIF loop | Low | Classic lofi aesthetic | | Video loop (10-30 min) | Medium | Professional look | | Live animation | High | Unique, engaging |
A 10-30 minute video loop works best. It's long enough that viewers don't notice the repeat point, and short enough to keep file sizes manageable.
Adding Information Overlay
Display the current track name and artist:
ffmpeg -re -stream_loop -1 -i background.mp4 -f concat -safe 0 -i playlist.txt -vf "drawtext=textfile=current_track.txt:reload=1:fontsize=20:fontcolor=white:x=50:y=h-50:font=Arial" -c:v libx264 -preset veryfast -b:v 3000k -c:a aac -b:a 192k -f flv "rtmp://a.rtmp.youtube.com/live2/YOUR_KEY"
The reload=1 flag makes FFmpeg re-read the text file every frame, so you can update the track name from an external script.
Track Name Updater
import time
import os
playlist = [
"Chill Vibes - Artist A",
"Rainy Afternoon - Artist B",
"Study Session - Artist C",
]
track_duration = 180 # 3 minutes average
for i, track in enumerate(playlist):
with open('current_track.txt', 'w') as f:
f.write(f"Now Playing: {track}")
time.sleep(track_duration)
Complete VPS Setup
Directory Structure
/home/lofi/
start.sh # Main startup script
playlist.txt # FFmpeg concat playlist
current_track.txt # Currently playing track
tracks/ # Audio files
visuals/ # Background video loops
scripts/ # Automation scripts
Systemd Service
Create a service that starts the stream automatically and restarts on failure:
[Unit]
Description=Lofi Radio Stream
After=network.target
[Service]
Type=simple
User=lofi
ExecStart=/home/lofi/start.sh
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
Resource Requirements
| Component | CPU Usage | RAM Usage | |-----------|-----------|-----------| | FFmpeg encoding (1080p) | 2-3 cores | 500MB | | Track management script | Negligible | 50MB | | Overlay rendering | 0.5 core | 100MB | | Total | 3-4 cores | ~1GB |
A 4-core Space-Node VPS runs this comfortably with overhead for monitoring and management.
Growth Strategy
Lofi streams grow slowly but steadily. Expect:
- Month 1-3: 5-20 concurrent viewers
- Month 3-6: 20-100 concurrent viewers
- Month 6-12: 100-500+ concurrent viewers
The key is consistency. A stream that's been running for 6 months straight gets algorithmic priority over one that goes on and off.
Make sure your music is properly attributed (if CC licensed) or fully licensed. YouTube will strike your live stream for copyright violations, and a strike on a 24/7 stream means all those accumulated viewers disappear instantly.
