Video has become a go-to format for modern developer education—but the user experience of learning hasn’t kept pace. What’s holding education platforms back from innovating on the learning experience?
Cats with Bats is out to change that. They’re building a next-gen learning platform designed specifically for developer educators, helping them to share in-depth tutorials about LLMs, RAG agents, and much more. In this post, Hai from Cats with Bats shares his vision for the future of learning to code and how Mux is helping bring it to life.
What inspired you to build Cats with Bats?
Before founding the company, I was a full-stack engineer at a YC startup while also teaching part-time at a coding school. It wasn’t a bootcamp-style program—it was 1-on-1 coaching, a much more personal experience. Watching someone transition from carpentry or construction into their first coding interview and then land a full-time role was something that never stopped amazing me. We’d meet once a week, and they’d spend the rest of their time building their project, learning by doing.
That experience left me wondering: how can learners get the best possible experience even when an instructor isn’t with them?
A 10-minute coding tutorial can be packed with insights. You might come looking for one specific answer but walk away with so much more. I built Cats with Bats to make coding videos easier to understand and faster to implement, so learners can get the most out of every session.
That’s why I built Cats with Bats to make coding videos easier to understand and faster to implement.
This is not my first rodeo with video; I was a film major at Denison University (Steve Carell’s alma mater). Before I became a full-stack engineer, I spent my time making music videos and documentaries. Now, I get to bring both worlds together. I’m a video nerd and a developer, which made choosing Mux an easy decision. Their team stood out because they care deeply about both.
What tech stack did you choose for Cats with Bats?
The platform is built on two core frameworks: Next.js for the application layer and business logic, and FastAPI for video processing and AI-powered features.
Creators upload their videos, and they go straight into Mux’s asset management. When learners watch, playback is handled through Mux’s Player.style—which, by the way, is one of the hidden gems of the internet. Since the stack is split across both JavaScript and Python, I use Mux’s TypeScript SDK on the frontend and built a custom Python client for backend integration.
I started out by having my app poll Mux’s servers until a video was ready for playback—a classic rite of passage. But as the AI-driven video processing pipeline grew more complex, I switched to using webhooks in both Next.js and FastAPI to streamline the workflow.
What makes the Cats with Bats learning experience different?
It's designed for modern developers who have learned to embrace AI tooling in their workflow. Need to use a lesson as context? Just hit CMD + C and the entire lesson is copied as an agent instruction, ready to drop into Cursor IDE or GitHub Copilot.
Want to skip the video and get straight to the answer? Just ask AI a question, and it will return the exact timestamp where the explanation happens. Mux’s video player works perfectly with it. Prefer reading instead? Every video has a blog version. It’s a more ergonomic approach compared to traditional course platforms that try to serve everyone the same way.
One fun project I’m working on right now is an MCP server that lets AI coding agents ping Cats with Bats, instantly learn from a video lesson, and then generate code for users. Wild times!
I also get a lot of emails about X-ray, one of the platform’s most-loved features. X-ray lets learners copy code straight from the video frame with a single click. If an instructor is showing a code snippet at any point in the video, you can grab it instantly without pausing or manually retyping it. One customer called it “the coolest thing I’ve ever seen done with videos.”
Initially, I built X-ray by processing video frames myself. When Mux sent a webhook event that an asset was ready, I kicked off a job to extract key frames. I started with FFMPEG, then switched to OpenCV for a speed boost. The system would take screenshots at critical moments, pass them to an AI model fine-tuned for recognizing code, and run OCR on them.
# Open video file with hardware acceleration
cap = cv2.VideoCapture(local_video_path, cv2.CAP_FFMPEG)
if not cap.isOpened():
raise ValueError("Failed to open video file")
# Enable hardware acceleration if available
cap.set(cv2.CAP_PROP_HW_ACCELERATION, cv2.VIDEO_ACCELERATION_ANY)
# Smaller buffer for memory constraints
cap.set(cv2.CAP_PROP_BUFFERSIZE, 5)
fps = cap.get(cv2.CAP_PROP_FPS)
# Process frames in smaller batches
frames_batch = []
for shot in shots_batch:
timestamp = shot.start + ((shot.end - shot.start) * 0.5)
frame_number = int(timestamp * fps)
cap.set(cv2.CAP_PROP_POS_FRAMES, frame_number)
ret, frame = cap.read()
if not ret:
logger.error(f"Failed to read frame for shot {shot.shot_index}")
continue
# Resize frames to conserve memory
height, width = frame.shape[:2]
if width > 1280: # Limit to 720p equivalent
scale = 1280 / width
frame = cv2.resize(frame, None, fx=scale, fy=scale)
frames_batch.append((frame, shot))
# Release video capture early
cap.release()
This preprocessing step could take anywhere from a few seconds to hours, depending on the video length and was susceptible to CPU bottlenecks. It worked, but there was clearly still room for improvement.
Then, a conversation with a co-founder at Mux led to a breakthrough that changed everything.
Why did you choose Mux to power your video course platform?
Before Mux, I was using Cloudinary, but I ran into some friction when it came to pricing and overall flexibility. That’s what led me to explore other options, and Mux immediately stood out with clear, direct documentation, practical use case guides, and a developer experience that didn’t get in the way.
Mux also made the transition simple. Even though I had already set up Cloudinary’s SDK with S3, switching over was surprisingly easy. And as it turned out, I didn’t even need S3 anymore. Mux handled everything I needed, right out of the box.
In late 2024, I had a call with Matt McClure, who leads Developer Experience at Mux. My goal was to convince him to let Cats with Bats into their startup program, which helps cover video streaming costs for startups. That’s when I learned about the Powered by Mux program. If you see a Mux logo on the video player at Cats with Bats, that’s because we’re now part of the program, which provides support for video-based platforms that benefit developer education.
During that same conversation, I mentioned the FFMPEG/OpenCV pipeline I had built for X-ray. Processing frames this way could take hours at times, especially with CPU bottlenecks on my serverless functions. Matt suggested I look into the Mux thumbnail API, an endpoint designed for quickly grabbing frames from a video for thumbnails. But in theory, I could also use it to request snapshots at precise timestamps for my AI model to process, completely skipping the need for manual screenshot extraction.
@dataclass
class ScreenshotOptions:
"""Options for customizing MUX screenshots."""
time: float | None = None
width: int | None = None
height: int | None = None
rotate: int | None = None
fit_mode: FitMode | None = None
flip_v: bool | None = None
flip_h: bool | None = None
format: ImageFormat = ImageFormat.JPG
async def get_screenshots_at_intervals(
playback_id: str,
duration: float,
interval: float,
base_options: ScreenshotOptions | None = None,
) -> list[tuple[float, BytesIO | None]]:
"""Get screenshots from a MUX video at regular intervals.
Args:
playback_id: The MUX playback ID
duration: Total duration of the video in seconds
interval: Time between screenshots in seconds
base_options: Base screenshot options to apply to all screenshots
Returns:
List of tuples containing (timestamp, image_data)
"""
times = [float(t) for t in range(0, int(duration), int(interval))]
if not times:
return []
return await get_screenshots_at_times(playback_id, times, base_options)
With a simple request to Mux’s server, I could retrieve exactly the frame I needed, apply any necessary transformations, and move on. No more OpenCV pipeline. I built a quick wrapper, and just like that, this step in the video-AI processing pipeline was 20 times faster.
Suddenly, new possibilities opened up. With such a massive speed boost, I started thinking about more ways to enhance the learning experience with AI-driven video features.
Building an AI-powered video platform is already complex, but Mux’s developer-first approach has taken a huge load off my plate. It lets me focus on real innovation, rather than fighting infrastructure problems. There’s still so much I want to do with AI and video, and without Mux’s backend handling the hard parts, I wouldn’t have even planned for some of these ideas.
One example is AI-generated video chaptering. Mux made it easy to implement (they even have a guide on it). And another thing I didn’t realize until recently: Mux also acts as my video’s cold storage. That’s one more problem solved without me having to think about it.
Where can someone try out the course platform?
If you’re a developer educator, you can upload video tutorials for free at www.catswithbats.com
If you’re looking to learn about AI engineering, head over to the Discover page: https://app.catswithbats.com/discover
And if your company builds developer-focused products and you're exploring developer education as part of your go-to-market strategy, let’s talk! Cats with Bats helps companies like Tavily (deep research for LLMs) and Vectorize (RAG pipeline workflows) host their Academy, making it easier to educate developers and build communities.