Lightweight HTML5 audio player for voice messages and audio content on the 3speak/Snapie ecosystem. Features waveform visualization using WaveSurfer.js and IPFS content delivery.
- π¨ WhatsApp/Discord-style UI - Clean, mobile-first interface
- π Waveform Visualization - Interactive audio scrubbing with WaveSurfer.js
- β‘ IPFS-powered - Decentralized audio storage with automatic gateway fallback
- π Dual Access Modes - Support for both permlinks (database) and direct CID playback
- π± Mobile-optimized - Touch controls, responsive design
- β© Speed Control - 1x, 1.5x, 2x playback speeds
- π Play Tracking - MongoDB-based analytics and engagement metrics
- π Rate-limited - Protection against API abuse
- π΅ Content Categories - Distinguish between ephemeral voice messages and permanent content (podcasts, songs, interviews)
- πΌοΈ Thumbnail Support - Optional cover art/artwork URLs for audio entries
- π‘ Feed API - Public endpoints for building discovery feeds, trending pages, tag browsing, and user profiles
- π·οΈ Tag System - Content tagging for discovery and filtering
Player (Frontend) βββ Express API βββ MongoDB βββ IPFS Network
β β β β
WaveSurfer.js Audio Routes Metadata Audio Files
HTML5 Audio Controllers Schema (CID-based)
- Node.js v16+ and npm
- MongoDB v5+ (local or remote)
- IPFS Gateway access (defaults to ipfs.3speak.tv)
git clone <repository-url>
cd snapieaudio
npm installcp .env.example .envEdit .env with your settings:
PORT=3000
MONGODB_URI=mongodb://localhost:27017/snapieaudio
IPFS_PRIMARY_GATEWAY=https://ipfs.3speak.tv/ipfs/# Development mode (with nodemon)
npm run dev
# Production mode
npm startOpen your browser:
# Direct CID playback (no database needed)
http://localhost:3000/play?cid=bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabq...
# Permlink-based playback (requires database entry)
http://localhost:3000/play?a=dkojohs
snapieaudio/
βββ src/ # Frontend audio player
β βββ index.html # Player page template
β βββ audio-player.js # Player logic (WaveSurfer)
β βββ audio-styles.css # Player styling
βββ server/ # Backend API
β βββ index.js # Express server
β βββ routes/
β β βββ audio.js # Audio API routes
β βββ controllers/
β β βββ audioController.js
β βββ models/
β βββ AudioMessage.js # MongoDB schema
βββ assets/ # Static assets (icons, etc.)
βββ docs/ # Internal documentation (gitignored)
β βββ AUDIO_PLAYER_DESIGN.md
βββ package.json
βββ .env.example
GET /api/audio?a=dkojohs
GET /api/audio?cid=bafybeig...
Response:
{
"permlink": "dkojohs",
"author": "meno",
"cid": "bafybeigdyrzt5...",
"category": "voice_message",
"duration": 45,
"format": "mp3",
"waveform": [0.2, 0.5, 0.8, ...],
"title": "My Audio",
"description": "This is an example audio recording",
"tags": ["music", "podcast", "interview"],
"thumbnail_url": "https://files.hive.blog/file/hiveimages/cover.jpg",
"post_permlink": "my-audio-snap-2026",
"audioUrl": "https://ipfs.3speak.tv/ipfs/bafy...",
"audioUrlFallback": "https://dweb.link/ipfs/bafy...",
"plays": 150
}GET /api/audio/feed?limit=20&offset=0&sort=newest
Query Parameters:
- limit: Items per page (default 20, max 100)
- offset: Pagination offset (default 0)
- sort: newest|oldest|plays|trending (default newest)
- category: voice_message|podcast|song|interview|audiobook|noise_sample
- tag: Filter by tag (e.g., "music")
- owner: Filter by username
Response:
{
"items": [
{
"permlink": "abc123xy",
"owner": "meno",
"audio_cid": "QmdMsEX...",
"category": "podcast",
"duration": 1800,
"format": "mp3",
"title": "My Podcast",
"tags": ["podcast", "tech"],
"thumbnail_url": "https://...",
"plays": 152,
"createdAt": "2026-02-20T10:30:00Z",
"audioUrl": "https://ipfs.3speak.tv/ipfs/...",
...
}
],
"pagination": {
"limit": 20,
"offset": 0,
"total": 156,
"hasMore": true
}
}
Examples:
GET /api/audio/feed?sort=plays # Trending audio
GET /api/audio/feed?category=podcast # All podcasts
GET /api/audio/feed?tag=music&sort=plays # Trending music
GET /api/audio/feed?owner=meno # User's audioPOST /api/audio/play
Content-Type: application/json
{
"permlink": "dkojohs"
}
Response:
{
"success": true,
"plays": 151
}{
permlink: "dkojohs", // Unique 8-char ID
author: "meno",
cid: "bafybeigdyrzt5...", // IPFS CID
category: "voice_message", // Content type: voice_message, podcast, song, interview, audiobook, noise_sample
duration: 45, // Seconds
format: "mp3",
title: "My Audio",
description: "This is an example audio recording",
tags: ["music", "podcast"], // Array of content discovery tags
thumbnail_url: "https://files.hive.blog/...", // Optional cover art
post_permlink: "my-audio-snap-2026", // Optional blockchain post reference
waveform: {
peaks: [0.2, 0.5, ...],
length: 100
},
plays: 150,
created: ISODate("2025-11-26T10:30:00Z"),
status: "active"
}<iframe
src="https://audio.3speak.tv/play?a=dkojohs"
width="600"
height="100"
frameborder="0">
</iframe>const AudioMessage = ({ permlink }) => (
<iframe
src={`https://audio.3speak.tv/play?a=${permlink}`}
style={{ border: 'none', width: '100%', height: '100px' }}
allow="autoplay"
/>
);npm start # Start production server
npm run dev # Start development server with nodemon
npm test # Run tests (to be implemented)Create a sample audio entry in MongoDB:
db.audio_messages.insertOne({
permlink: "testaud1",
author: "testuser",
cid: "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabq...",
duration: 45,
format: "mp3",
bitrate: 128,
waveform: { peaks: Array(100).fill(0).map(() => Math.random()), length: 100 },
title: "Test Audio",
plays: 0,
created: new Date(),
status: "active",
visibility: "public"
});Test it:
http://localhost:3000/play?a=testaud1
| Variable | Default | Description |
|---|---|---|
PORT |
3000 | Server port |
MONGODB_URI |
mongodb://localhost:27017 | MongoDB connection |
DB_NAME |
snapieaudio | Database name |
IPFS_PRIMARY_GATEWAY |
ipfs.3speak.tv/ipfs/ | Primary IPFS gateway |
IPFS_FALLBACK_GATEWAY_1 |
dweb.link/ipfs/ | Fallback gateway #1 |
IPFS_FALLBACK_GATEWAY_2 |
cloudflare-ipfs.com/ipfs/ | Fallback gateway #2 |
RATE_LIMIT_WINDOW_MS |
60000 | Rate limit window |
RATE_LIMIT_MAX_REQUESTS |
100 | Max requests per window |
Indexes are automatically created on startup:
db.audio_messages.createIndex({ permlink: 1 }, { unique: true })
db.audio_messages.createIndex({ author: 1, created: -1 })
db.audio_messages.createIndex({ cid: 1 })
db.audio_messages.createIndex({ created: -1 })- Landing Page:
https://audio.3speak.tv/- Project overview and features - Demo Upload:
https://audio.3speak.tv/demo- Try uploading audio (24h expiry) - Admin Panel:
https://audio.3speak.tv/admin- Storage management (password protected) - Player:
https://audio.3speak.tv/play?a=PERMLINK- Audio playback
- Basic player with WaveSurfer.js
- MongoDB schema & API endpoints
- CID-based playback
- IPFS gateway fallback
- Play tracking
- Audio upload endpoint with API key auth
- IPFS pinning (local node)
- Permlink auto-generation
- File validation & processing
- Demo upload page
- Admin storage management panel
- Speed control UI
- Download functionality
- Share buttons
- Mobile app integration
- React component library
- Snapie chat integration
- Hive blockchain support
- Podcast RSS feeds
-
Check IPFS gateway is accessible:
curl https://ipfs.3speak.tv/ipfs/<your-cid>
-
Verify MongoDB connection:
mongosh mongodb://localhost:27017/snapieaudio db.audio_messages.find() -
Check browser console for errors
If embedding in external sites, update CORS settings in server/index.js:
app.use(cors({
origin: ['https://yourdomain.com'],
credentials: true
}));Contributions welcome! Please:
- Fork the repository
- Create a feature branch
- Make your changes
- Submit a pull request
MIT License - see LICENSE file for details
- WaveSurfer.js - Audio waveform visualization
- 3speak - Decentralized video platform
- IPFS - Distributed file system
For questions or support, reach out to the 3speak/Snapie team.
Built with β€οΈ for the 3speak/Snapie ecosystem