Skip to main content

Command Palette

Search for a command to run...

From Sync to Summary: Building Meetly, Meetings with Memory.

Updated
4 min read
From Sync to Summary: Building Meetly, Meetings with Memory.
S
Full Stack Developer | AI Enthusiast | Builder 🛠️ Currently focused on building high-performance products like ShortIQ (AI content engine) and Spectr (real-time analytics). I write about Next.js, backend architecture, and the journey of scaling modern web apps. Tech Stack: JavaScript/TypeScript, Next.js, Node.js, PostgreSQL, and AI integration (Gemini/Sarvam). Check out my work: subhashjha.me

Meetings are where ideas are born, but often where they are forgotten. We’ve all been there: a 45-minute sync ends, you hang up, and 10 minutes later, you’re scratching your head trying to remember exactly what was decided or who was assigned that critical task.

This is why I built Meetly.

Meetly isn't just another video conferencing tool. It’s a "Meeting with Memory"—a platform that captures audio, transcribes it in real-time, and uses AI to extract structured summaries and action items the second you hang up.

In this post, I’ll walk you through the technical journey, the challenges I faced, and how I built a custom design system to give it a unique, industrial look.


The Tech Stack

To build a high-performance, real-time application, I chose a modern and scalable stack:

  • Framework: Next.js 15 (App Router)

  • Real-time Communication (RTC): LiveKit

  • Database: PostgreSQL with Prisma ORM

  • Authentication: NextAuth.js (v5)

  • Styling: Tailwind CSS + Framer Motion

  • AI Pipeline: Custom LLM integration for summarization and action-item extraction


Challenge 1: Escaping the Generic SaaS Look

Most meeting apps look the same—a grid of faces with some rounded buttons. For Meetly, I wanted something that felt more like a "Developer Tool" or a "Command Center."

Instead of standard "glassmorphism" or generic cards, I implemented an Industrial Minimalist design.

The Solution: UI Primitives

I created a set of shared primitives that use:

  • Crosshair Corner Markers: To give elements a precise, technical feel.

  • Section Corner Brackets: Instead of full borders, I used L-shaped brackets to define space.

  • Zinc Palette: A strictly neutral, dark-mode-first aesthetic using #0a0a0a.

Here’s a snippet of how the CrosshairCorner primitive works:

export function CrosshairCorner({ position, size = 6, color = 'bg-zinc-500' }) {
  return (
    <>
      <div className={`absolute \({POSITIONS[position]} w-[1px] \){color}`} style={{ height: size }} />
      <div className={`absolute \({POSITIONS[position]} h-[1px] \){color}`} style={{ width: size }} />
    </>
  );
}

Challenge 2: The "Silent Participant" Bug (LiveKit Permissions)

One of the trickiest bugs I faced was during the LiveKit integration. Participants could join the room perfectly, see others, and hear audio—but they couldn't turn on their own cameras or microphones.

The Debugging Process

After digging into the LiveKit server-side SDK, I realized that the tokens I was generating for participants had restricted permissions by default.

The Fix

I had to refactor the token generation logic to explicitly grant canPublish and canPublishData permissions based on the user's role in the meeting.

const at = new AccessToken(apiKey, apiSecret, {
  identity: participantIdentity,
});

at.addGrant({
  room: roomName,
  roomJoin: true,
  canPublish: true,      // The critical missing piece
  canPublishData: true,  // For real-time AI features
});

Challenge 3: Connecting the AI Pipeline

The core value of Meetly is the AI Summarization. The challenge was managing the flow of data:

  1. Capture high-quality audio via LiveKit.

  2. Send audio chunks to a transcription engine.

  3. Once the meeting ends, feed the full transcript into an LLM.

  4. Extract specific Action Items and Key Decisions.

I learned that prompt engineering is just as important as the backend logic here. A generic "summarize this" prompt results in a wall of text. Instead, I used structured prompts to ensure the output was always bulleted and categorized by "Decisions" and "Tasks."


Challenge 4: The Production Wall (Vercel & Hydration)

Moving from localhost to production on Vercel brought its own set of headaches.

  1. Case-Sensitivity: My local machine (Windows) didn't care if an image was named Hero.png or hero.png, but Vercel’s Linux environment did. This caused multiple failed builds until I standardized all asset naming.

  2. Hydration Errors: Integrating interactive components like GitHub-style calendars caused mismatches between the server-rendered HTML and the client-side state. I solved this by implementing isMounted checks to ensure client-only components only rendered after the initial paint.


Conclusion

Building Meetly has been a massive learning experience in blending Real-Time Communication with AI. It taught me that design isn't just about colors—it's about the "feel" and utility of the tool.

Moving forward, I'm looking into multi-language transcription and deeper integrations with tools like Slack and Notion.

Check out the project here:

[Github: https://github.com/subhash-jhaa/meetly

Live URL: https://trymeetly.vercel.app ]

What do you think about the industrial aesthetic? Let’s discuss in the comments!

2 views

More from this blog

B

Blogs

2 posts

A developer blog by Subhash Jha — full-stack developer passionate about building open-source tools, SaaS products, and modern web experiences. Writing about Next.js, Node.js, system design, and the journey of growing as a developer.