WatchSankofa
The African-First Streaming Platform — 85% Revenue Share for Creators
Video streaming platform for African creators built on the Sankofa principle: go back and fetch what was lost. 85% creator revenue share vs Netflix's ~7%. Flutterwave payouts, isiXhosa content support, African cinematic design system.
Serving
African content creators continent-wide
Market
85% revenue share vs 7% Netflix
Problem
No African streaming home exists
Result
Sankofa: reclaiming what was lost
What needed solving
African creators build audiences on platforms built for Hollywood. Netflix pays creators ~7% revenue share. YouTube's algorithm has no concept of African cultural content. There is no streaming home built for the continent.
How I built it
A full-stack streaming platform with Cloudinary video processing, 85% revenue share for creators, Flutterwave Africa-native payouts, language-first content discovery, and a cinematic dark design system built around African visual identity.
WatchSankofa began as a question during the YouTube clone: what does YouTube get wrong for African creators? Every layout decision was studied. The algorithm has no concept of isiZulu spoken word poetry as a distinct cultural form. An African filmmaker performing in Xhosa has the same discoverability as a gaming livestream. WatchSankofa was built to fix that. The Phase 1 foundation was static HTML/CSS/JS — a landing page and product spec ('AFRIFLIX_MASTER_PROMPT.md') that seeded everything. Phase 2 was a full Next.js 16.1.7 rebuild started 2026-03-18. Every architecture decision in Phase 2 was written with a 'why' rationale. The audio player architecture decision: Zustand with persist middleware over Redux or Context. The audio player must persist across navigation without remounting — a user browsing while listening cannot have the player restart mid-track on every route change. Zustand's persist syncs to localStorage. Redux is overkill; Context re-renders the entire tree on every state change — catastrophic for a media player. The video player is custom, not react-player. Control: WatchSankofa branding in the player, keyboard shortcuts that feel native, fullscreen with the container not the viewport, and the ability to swap the underlying video source to Cloudflare Stream's HLS without rewriting the UI. Server Components + client islands: content browsing, creator profiles, and search are server-rendered (no JavaScript for these read-heavy views). The audio player and video player are client islands. This is strictly better than a React SPA for a content platform where 80%+ of interactions are read operations.
// Zustand audio player — persists across navigation without remounting
// Context would re-render the entire component tree on every state change
// Redux is overkill; Zustand's persist handles SSR hydration cleanly
interface AudioPlayerState {
currentTrack: Track | null;
isPlaying: boolean;
progress: number;
volume: number;
queue: Track[];
play: (track: Track) => void;
pause: () => void;
seek: (seconds: number) => void;
enqueue: (track: Track) => void;
}
export const useAudioPlayer = create<AudioPlayerState>()(
persist(
(set, get) => ({
currentTrack: null,
isPlaying: false,
progress: 0,
volume: 0.8,
queue: [],
play: (track) => set({ currentTrack: track, isPlaying: true }),
pause: () => set({ isPlaying: false }),
seek: (seconds) => set({ progress: seconds }),
enqueue: (track) => set((s) => ({ queue: [...s.queue, track] })),
}),
{
name: 'watchsankofa-player',
storage: createJSONStorage(() => localStorage),
// skipHydration prevents SSR/client mismatch — call rehydrate() after mount
skipHydration: true,
// Only persist these fields — not UI state like isPlaying
partialize: (s) => ({ currentTrack: s.currentTrack, volume: s.volume, queue: s.queue }),
}
)
);What This Taught Me
- 1
Zustand + persist is the correct pattern for a media player: persists across navigation, no SSR mismatch with skipHydration, no Redux overhead
- 2
Server Components for read-heavy views (browsing, profiles, search) + client islands for interactive UI (player) — minimum JavaScript shipped to the client
- 3
Custom video player over react-player: swap the source to Cloudflare Stream HLS without rewriting the UI; own the keyboard shortcuts and fullscreen behaviour
- 4
The Sankofa principle is not branding — it is the product specification: recover what African creators were denied (ownership, revenue, visibility)
- 5
85% creator revenue share signals whose side the platform is on before a single feature ships
Next.js 14
SSR for SEO + fast initial load on SA mobile data
TypeScript
Type safety for creator and content data models
Supabase
Content metadata, creator profiles, viewer analytics
Cloudinary
Video processing, transcoding, adaptive streaming
Flutterwave
Africa-native creator payouts (ZAR, NGN, KES, GHS)
Tailwind CSS
Cinematic dark theme — mahogany, amber, deep red
# WatchSankofa > The African-First Streaming Platform — 85% Revenue Share for Creators ## Project Context **Category:** Streaming · Creative Technology · Social Impact **Status:** Beta · 2025 **Author:** Nandawula Regine Kabali-Kagwa — East London, South Africa **Company:** Mirembe Muse (Pty) Ltd · Reg: 2026-005658 ## Stack ``` Next.js 14 # SSR for SEO + fast initial load on SA mobile data TypeScript # Type safety for creator and content data models Supabase # Content metadata, creator profiles, viewer analytics Cloudinary # Video processing, transcoding, adaptive streaming Flutterwave # Africa-native creator payouts (ZAR, NGN, KES, GHS) Tailwind CSS # Cinematic dark theme — mahogany, amber, deep red ``` ## Architecture Notes - All data mutations validated server-side via Next.js API routes - Row-Level Security enforced at database level (Supabase) - Mobile-first, PWA-ready, offline-tolerant where connectivity is unreliable - PayFast integration for ZAR-native payments (no USD conversion) - SEO-optimised: metadata, JSON-LD, canonical URLs, sitemap - POPIA compliant — data minimisation + user consent by design ## Environment Variables ```env NEXT_PUBLIC_SUPABASE_URL= NEXT_PUBLIC_SUPABASE_ANON_KEY= SUPABASE_SERVICE_ROLE_KEY= ANTHROPIC_API_KEY= NEXT_PUBLIC_PAYFAST_MERCHANT_ID= NEXT_PUBLIC_PAYFAST_MERCHANT_KEY= PAYFAST_PASSPHRASE= RESEND_API_KEY= ``` ## Links - Live: Coming soon — domain propagating - GitHub: Private repository - Portfolio: https://creativelynanda.co.za/projects/watchsankofa --- Built from East London, South Africa · Nine months · Zero to production
More Projects
Interested in similar work?
Let's discuss how I can build something like this for your business.
Let's Talk