I'm a big Twitch viewer and part-time streamer, and I've always felt like something was missing. Twitch is amazing for live streams, but once the stream ends, the community disperses across Discord, Reddit, BlueSky, Threads and Twitter. There is no single platform where Twitch streamers and their communities can stay engaged between streams.
So I built Chattr, a social network for Twitch users to share posts, react with Twitch emotes, get live alerts, and stay connected with streamers and viewers.
Here's how I built it.
The Stack
Backend: Laravel 13 + PHP 8.5
Laravel is fast, flexible, and supplies a robust ecosystem. PHP 8.5 with Laravel 13 introduces constructor property promotion, typed properties, and attribute-based features such as #[Fillable].
A few Laravel packages doing heavy lifting:
- Laravel Socialite enables Twitch OAuth in just 20 lines of code. Users will sign in with their Twitch account; no new password or username is required. On sign-in, Twitch automatically syncs followers, so your community is ready. Also, if you are a partner with a Twitch-verified badge, your badge will carry over.
- Laravel Reverb provides real-time features. Twitch fires a webhook when a streamer goes live, which then broadcasts an event, instantly updating every follower's feed. The same applies to posts, reactions, and comments.
- Laravel Scout - full-text search for users and posts, backed by a database driver for simplicity at this stage.
- Laravel Sanctum - API token authentication for the mobile apps.
- Laravel Nightwatch - production monitoring and error tracking without the third-party overhead.
- Laravel Wayfinder - auto-generates TypeScript functions for every Laravel route and controller action. The frontend avoids hardcoded URLs; each route call becomes a typed function import. Major win for refactoring safety.
Frontend: React 19, Inertia.js v3 and Tailwind CSS v4
Inertia bridges Laravel and React, allowing me to build a client-side React single-page application without separate APIs. I return typed page components from controller actions, share global state, and navigate entirely with Laravel routes. No API versioning or serialisation layer was required.
React 19 and its compiler, via babel-plugin-react-compiler, automate memoisation and reduce useMemo and useCallback boilerplate.
Tailwind CSS v4 eliminates the config file in favour of a CSS-native approach. I define design tokens in a single CSS file, and Tailwind automatically recognises them. Greatly reducing config effort for a project I'm building solo.
Real-time with WebSockets
Live stream detection is the feature I'm most proud of. Here's the flow:
- When a user connects their Twitch account, they subscribe to Twitch EventSub webhooks for their channel.
- When a Twitch stream goes live, the website creates a live post, broadcasts the event via Reverb, and sends push notifications to followers.
- On the frontend, Laravel Echo updates the feed in real time: the live post appears, the avatar gets a red ring, and a notification is sent.
All live updates are delivered via a single WebSocket connection managed by Reverb. No third-party services or extra messaging costs.
Video: Mux
Mux handles user-uploaded videos. Files are uploaded directly from the browser, processed asynchronously, and a webhook is fired when processing is complete. The website stores the playback ID, streams directly from Mux's CDN, and gets transcoding, dynamic bitrate, and signed URLs without managing codecs.
Payments: Stripe Connect
Streamers can get tips through Stripe Connect Express in the app. Stripe handles onboarding, KYC (Know Your Customer), payouts, and compliance. Tips: Use the Payment Intent flow, and platform fees are handled at the charge level. The app doesn't store any bank/credit card information, as all payments are handled via Stripe. For now, those tipped get 70%, while I get the remaining 30% plus any fees Stripe charges.
I've done this because the hosting needs to earn money, and I felt this was the least intrusive way to potentially make money back. In the future, I would like to offer special sponsorships that cover hosting costs, and then I will change the split so creators can earn 100% of their tips.
Infrastructure: AWS
Hosting with Amazon ECS
The app runs in Amazon ECS with Fargate. Serverless containers, no EC2 to manage. I use Docker to containerise Laravel, deploying separate ECS services for the web server (FrankenPHP/Caddy), the queue worker, and Reverb WebSocket.
FrankenPHP is a modern PHP server built on Caddy. It offers faster cold starts, supports HTTP/2, HTTP/3, Early Hints, and natively handles WebSocket upgrades.
ECS Fargate lets me scale the web tier independently from the queue workers, which is important for a social platform where queue throughput surges when many users go live simultaneously (weekend evenings are intense).
File Storage: Amazon S3
All user media-images, audio, alert sounds-are stored in S3. Uploads go directly from browser to S3 with pre-signed URLs, so Laravel never handles the raw bytes. This keeps requests fast and offloads file processing.
The S3 bucket is private. All media is served through pre-signed URLs with short expiry, so subscriber-only content stays locked even if someone tries to share the URL directly. This is something I'm particularly proud of, because it's hard to keep videos private a lot of the time.
CDN: Amazon CloudFront
CloudFront caches all app and S3 static assets globally. Users worldwide experience fast loading, regardless of their region.
CloudFront terminates SSL and routes traffic to ECS. Dynamic HTML uses conservative cache policies; hashed static assets are cached for a year.
Features Worth Highlighting
Twitch Emote Reactions
Rather than thumbs-up and hearts, users react to posts with actual Twitch emotes. I fetch the global Twitch emote set on first load and store it in memory. Each reaction records the emote slug and displays as an image from Twitch's CDN. This feature deeply cultivates community - it's the language Twitch communities already use.
Subscriber-Only Posts
Streamers can lock any post for Twitch subscribers. Subscriber status is verified live on every load; if it lapses, access is revoked automatically.
Public Streamer Directory
I added a public streamer directory at chattr.online/streamers. Users can opt in to have their profiles and posts indexed by Google. This helps SEO-"{game} streamers" searches surface Chattr profiles.
Voice Posts with AI Transcription
Users can record audio posts. AWS will then transcribe it automatically with OpenAI Whisper and attach a searchable transcript to the post. Users can either read or listen to the post. This was inspired by Threads, which used to have Voice Notes but later removed them. Threads users have been calling for the return of Voice Notes, so I thought it would be a good addition.
What I would Do Differently
I wish I had started public-facing pages earlier. Building the social feed first put everything behind a login wall. Search engines couldn't crawl the content. Adding the streamer directory and public profiles boosted discovery immediately.
Try It
Chattr is live at chattr.online. Sign in with Twitch for instant access to the community. No setup, manual follows, or empty feed. People you already follow on Twitch appear in your feed if they're on Chattr.
If you're a streamer, it takes about 30 seconds to opt in to the public directory and start getting indexed. Game communities are automatically created the first time someone joins - so if you play a game and your fans do too, there's already a community page for it.
Built with Laravel, React, Inertia.js, Tailwind, AWS ECS, CloudFront, S3, Mux, Stripe, and a lot of Twitch streams in the background.
Questions or feedback? Find me on Chattr at chattr.online/streamers/MichaelBrooksUK or on Threads.
Top comments (0)