<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Zishan Ghaniwala</title>
    <description>The latest articles on DEV Community by Zishan Ghaniwala (@zishan_vsdk).</description>
    <link>https://dev.to/zishan_vsdk</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4049583%2F048cd163-4e07-4392-a00e-8e700e2275c2.jpg</url>
      <title>DEV Community: Zishan Ghaniwala</title>
      <link>https://dev.to/zishan_vsdk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zishan_vsdk"/>
    <language>en</language>
    <item>
      <title>Build ESP32 Video &amp; Audio Calling With VideoSDK</title>
      <dc:creator>Zishan Ghaniwala</dc:creator>
      <pubDate>Wed, 05 Aug 2026 12:10:09 +0000</pubDate>
      <link>https://dev.to/video-sdk/build-esp32-video-audio-calling-with-videosdk-3338</link>
      <guid>https://dev.to/video-sdk/build-esp32-video-audio-calling-with-videosdk-3338</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;ESP32 video calling runs two-way WebRTC audio and video directly on an ESP32-S3 microcontroller. The VideoSDK IoT SDK ships as an ESP-IDF component that joins your board to the same VideoSDK room a browser joins, with no signaling server of your own. Set a token, flash the firmware, and join from the web.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Until recently, ESP32 video calling meant building, hosting, and maintaining your own signaling and media backend before a single frame moved. The VideoSDK IoT SDK cuts out that infrastructure. It provides a native ESP-IDF component that hooks an ESP32-S3 directly into a WebRTC room, the exact same room a browser or mobile app joins.&lt;/p&gt;

&lt;p&gt;Your board streams camera and microphone input into the room, and on boards with a screen and speaker, it draws remote video on the LCD and plays remote audio back. By the end of this guide you will have an ESP32-S3 holding a live video and audio call with a web application, without managing a custom media server.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is ESP32 Video Calling?
&lt;/h2&gt;

&lt;p&gt;ESP32 video calling is defined as real-time, two-way audio and video streaming that originates and terminates on an ESP32 microcontroller rather than on a phone or laptop. It works by capturing frames from an onboard camera and samples from a microphone, encoding them on-device, and publishing them over WebRTC to every other participant in a room.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://components.espressif.com/components/videosdk/iot-sdk" rel="noopener noreferrer"&gt;VideoSDK IoT SDK&lt;/a&gt; provides this capability as a native ESP-IDF component for the ESP32-S3 family, published on Espressif's official component registry under the MIT license. It handles the room connection logic, WebRTC encryption (DTLS and SRTP), and media stream lifecycle, so your firmware manages calls through a handful of function calls.&lt;/p&gt;

&lt;p&gt;The board is a first-class participant, not a camera feeding a gateway. It appears in the participant list, it can be muted, and it shares the same room ID your &lt;a href="https://docs.videosdk.live/react/guide/video-and-audio-calling-api-sdk/quick-start" rel="noopener noreferrer"&gt;React&lt;/a&gt; or JavaScript client already uses.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyowoy6outlffla5515q0.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyowoy6outlffla5515q0.jpeg" alt="Architecture" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Need Before You Start
&lt;/h2&gt;

&lt;p&gt;The board you choose decides whether your device can only send media or handle full two-way playback, so pick it before writing any code.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Board&lt;/th&gt;
&lt;th&gt;Send (Mic &amp;amp; Camera)&lt;/th&gt;
&lt;th&gt;Receive (Speaker &amp;amp; LCD)&lt;/th&gt;
&lt;th&gt;Hardware Detail&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;XIAO ESP32-S3 (Sense)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Includes camera &amp;amp; mic, but lacks onboard display &amp;amp; speaker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ESP32-S3-Korvo-2 v3.0&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Includes camera, mic, LCD, and speaker for full two-way A/V&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;On the XIAO board, calling &lt;code&gt;startSubscribeVideo()&lt;/code&gt; or &lt;code&gt;startSubscribeAudio()&lt;/code&gt; returns &lt;code&gt;DEVICE_NOT_SUPPORTED&lt;/code&gt;. That is expected behavior for missing hardware, not a bug. Pick the Korvo-2 if you want the board itself to display and play back the far end.&lt;/p&gt;

&lt;p&gt;Prerequisites:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://docs.espressif.com/projects/esp-idf/en/stable/esp32s3/get-started/index.html" rel="noopener noreferrer"&gt;ESP-IDF 5.4.4 or newer&lt;/a&gt;&lt;/strong&gt; with the environment exported. The component declares 5.4.4 as its minimum.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Python 3.11+&lt;/strong&gt;, required by the ESP-IDF build tools. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;8 MB of flash and PSRAM&lt;/strong&gt; on the board. The application image does not fit in 4 MB.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;VideoSDK account&lt;/strong&gt; for your API key, secret, and room tokens.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How ESP32 Video Calling Works Under the Hood
&lt;/h2&gt;

&lt;p&gt;Running WebRTC on an ESP32 is a memory problem before it is a CPU problem. The WebRTC stack, the DTLS handshake state, video frames, and audio buffers all compete for internal SRAM, which is why supported boards carry external PSRAM and why the SDK depends on it for stable operation.&lt;/p&gt;

&lt;p&gt;WebRTC security runs end to end. The device performs a DTLS handshake and transmits media over encrypted SRTP streams, the same transport a browser uses.&lt;/p&gt;

&lt;p&gt;Receiving is where the SDK saves the most work. Publishing is comparatively simple, but subscribing means decoding incoming video and audio, absorbing network jitter, drawing to an LCD, and driving speaker hardware in real time inside tight SRAM limits. On the ESP32-S3-Korvo-2, the publish and subscribe calls for both audio and video run concurrently.&lt;/p&gt;

&lt;p&gt;Wi-Fi quality shows up directly in frame rate and audio clarity. Keep the antenna clear, connect to 2.4 GHz, and test on the network the hardware will actually run on in production.&lt;/p&gt;

&lt;h3&gt;
  
  
  When Not to Use the IoT SDK
&lt;/h3&gt;

&lt;p&gt;Espressif ships its own &lt;a href="https://github.com/espressif/esp-webrtc-solution" rel="noopener noreferrer"&gt;esp-webrtc-solution&lt;/a&gt;, and it wins in a specific case: you already run your own signaling server and media infrastructure, or you need to peer directly with a device that is not in a VideoSDK room. The IoT SDK assumes VideoSDK rooms and VideoSDK tokens, which is exactly the assumption that removes the backend work. If you want the board on an existing SFU you operate yourself, the managed path is the wrong trade.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Set Up the ESP-IDF Environment
&lt;/h2&gt;

&lt;p&gt;Install Espressif's ESP-IDF toolchain. The commands below cover macOS; see the official &lt;a href="https://docs.espressif.com/projects/esp-idf/en/stable/esp32s3/get-started/index.html" rel="noopener noreferrer"&gt;ESP-IDF getting started guide&lt;/a&gt; for Linux and Windows.&lt;/p&gt;

&lt;p&gt;Install build dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;cmake ninja dfu-util ccache git wget flex bison gperf
brew &lt;span class="nb"&gt;install &lt;/span&gt;openssl libffi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clone ESP-IDF at v5.4.4 or newer and run the installer. Older branches will not build this component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/esp &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; ~/esp
git clone &lt;span class="nt"&gt;--recursive&lt;/span&gt; &lt;span class="nt"&gt;-b&lt;/span&gt; v5.4.4 https://github.com/espressif/esp-idf.git
&lt;span class="nb"&gt;cd &lt;/span&gt;esp-idf
./install.sh esp32s3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Export the environment variables into your shell session:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source&lt;/span&gt; ~/esp/esp-idf/export.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Generate Your VideoSDK Token
&lt;/h2&gt;

&lt;p&gt;VideoSDK uses token-based authentication. For building and testing you do not need a signing server: the dashboard generates a token for you in about a minute.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the &lt;a href="https://dub.sh/X5Fn46e?utm_source=esp32-video-calling-videosdk-iot-sdk" rel="noopener noreferrer"&gt;VideoSDK dashboard&lt;/a&gt; and sign up or log in.&lt;/li&gt;
&lt;li&gt;Copy your &lt;strong&gt;API key&lt;/strong&gt; and &lt;strong&gt;secret&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Generate a &lt;strong&gt;temporary token&lt;/strong&gt; and save it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz1kz4ebnftbbgvesgb73.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz1kz4ebnftbbgvesgb73.png" alt="Generate a API key" width="800" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You also need a room (meeting) ID, and the order here matters. The React client in Step 7 mints one for you when you click &lt;strong&gt;New Meeting&lt;/strong&gt;, and that is the ID you flash onto the board.&lt;/p&gt;

&lt;p&gt;Firmware is harder to rotate than a web bundle, so a long-lived token flashed into a device is a liability. The &lt;a href="https://docs.videosdk.live/react/guide/video-and-audio-calling-api-sdk/authentication-and-token" rel="noopener noreferrer"&gt;VideoSDK authentication and token guide&lt;/a&gt; covers token lifetimes and permission scopes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Create the Project From the Example
&lt;/h2&gt;

&lt;p&gt;Fetch the project structure from the official &lt;a href="https://components.espressif.com/components/videosdk/iot-sdk" rel="noopener noreferrer"&gt;&lt;code&gt;video_call&lt;/code&gt;&lt;/a&gt; example, then extend it to carry audio as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;idf.py create-project-from-example &lt;span class="s2"&gt;"videosdk/iot-sdk=0.3.1:video_call"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The stock &lt;code&gt;video_call&lt;/code&gt; example captures camera input and draws incoming video on supported hardware such as the ESP32-S3-Korvo-2, running with &lt;code&gt;videoCodec = VIDEO_CODEC_JPEG&lt;/code&gt;. Step 5 adds the audio streams.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Configure the Build
&lt;/h2&gt;

&lt;p&gt;Set the target chip to &lt;code&gt;esp32s3&lt;/code&gt; and open menuconfig to enter credentials and Wi-Fi settings.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;idf.py set-target esp32s3
idf.py menuconfig
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmcr6qklxhg63pziu4eat.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmcr6qklxhg63pziu4eat.png" alt="menuconfig interface" width="800" height="579"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the menuconfig interface:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Microcontroller board:&lt;/strong&gt; Select &lt;code&gt;ESP32-S3-Korvo-2&lt;/code&gt; or &lt;code&gt;ESP32-S3-XIAO&lt;/code&gt; (XIAO is the default).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VideoSDK Configuration:&lt;/strong&gt; Paste the token and the meeting / room ID. If you are following the repo flow, get the room ID from the React client in Step 7 first, then come back here.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Example Connection Configuration:&lt;/strong&gt; Enter your 2.4 GHz Wi-Fi SSID and password.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flash and Partitions:&lt;/strong&gt; Set flash size to 8 MB and keep the partition table the example ships.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep &lt;code&gt;sdkconfig&lt;/code&gt; out of public repositories. It holds your Wi-Fi credentials and your VideoSDK token in plain text.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Understand the Code
&lt;/h2&gt;

&lt;p&gt;The firmware flow connects to Wi-Fi, fills an &lt;code&gt;init_config_t&lt;/code&gt; structure, calls &lt;code&gt;init()&lt;/code&gt;, and starts the media streams. The example ships as a video-only call, so its &lt;code&gt;app_main&lt;/code&gt; sets only the video codec and starts only the two video streams:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;"videosdk.h"&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;"sdkconfig.h"&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;app_main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Initialize NVS, network interfaces, event loops, and Wi-Fi...&lt;/span&gt;

    &lt;span class="n"&gt;init_config_t&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;meetingID&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CONFIG_VIDEOSDK_MEETING_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CONFIG_VIDEOSDK_TOKEN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;displayName&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"ESP32S3-AV-Device"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;participantId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;audioCodec&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AUDIO_CODEC_PCMA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;videoCodec&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;VIDEO_CODEC_JPEG&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Video only, as shipped&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;RESULT_OK&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;startPublishVideo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;     &lt;span class="c1"&gt;// Stream the onboard camera&lt;/span&gt;
    &lt;span class="n"&gt;startSubscribeVideo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;   &lt;span class="c1"&gt;// Draw to the LCD (Korvo-2 only)&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;vTaskDelay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pdMS_TO_TICKS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To turn this into a full audio and video call, add audio yourself. It is just one change. Start the two audio streams alongside the video ones:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;    &lt;span class="n"&gt;init_config_t&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;meetingID&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CONFIG_VIDEOSDK_MEETING_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CONFIG_VIDEOSDK_TOKEN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;displayName&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"ESP32S3-AV-Device"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;participantId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;audioCodec&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AUDIO_CODEC_PCMA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;videoCodec&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;VIDEO_CODEC_JPEG&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="c1"&gt;// ...after init() returns RESULT_OK:&lt;/span&gt;

    &lt;span class="n"&gt;startPublishAudio&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;     &lt;span class="c1"&gt;// Add: stream the onboard microphone&lt;/span&gt;
    &lt;span class="n"&gt;startPublishVideo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;startSubscribeAudio&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;   &lt;span class="c1"&gt;// Add: play to the speaker (Korvo-2 only)&lt;/span&gt;
    &lt;span class="n"&gt;startSubscribeVideo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That gives you all four streams for full A/V. Every SDK function returns a &lt;code&gt;result_t&lt;/code&gt; status code, where &lt;code&gt;RESULT_OK&lt;/code&gt; equals &lt;code&gt;0&lt;/code&gt;. On send-only hardware like the XIAO, &lt;code&gt;startSubscribeAudio()&lt;/code&gt; returns &lt;code&gt;DEVICE_NOT_SUPPORTED&lt;/code&gt;, which is expected. Check return codes in production firmware rather than assuming the stream came up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Build, Flash, and Verify the Join
&lt;/h2&gt;

&lt;p&gt;Compile the firmware, flash the board, and open the serial monitor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;idf.py build
idf.py &lt;span class="nt"&gt;-p&lt;/span&gt; &amp;lt;PORT&amp;gt; flash monitor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A successful boot and room join produces this sequence. Values are redacted; yours will show your own network details:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I (20151) wifi:connected with &amp;lt;YOUR_SSID&amp;gt;, aid = 7, channel 7, BW20, bssid = &amp;lt;REDACTED&amp;gt;
I (20152) wifi:security: WPA2-PSK, phy: bgn, rssi: -49
I (21194) esp_netif_handlers: example_netif_sta ip: &amp;lt;LOCAL_IP&amp;gt;, mask: 255.255.255.0
I (21205) example_common: Connected to example_netif_sta
I (21211) IOT-SDK-AUDIO: Device ID: e-ccba970e9220
I (21215) videosdk: participantId (peerId): e-ccba970e9220
I (21256) IOT-SDK-AUDIO: init: 0
I (23312) protoo: decrypted 4 ICE server(s)
I (23313) protoo: connecting websocket to in1.rm.videosdk.live
I (23562) esp-x509-crt-bundle: Certificate validated
I (24094) protoo: websocket connected
I (24113) protoo: protoo client ready (peerId=e-ccba970e9220)
I (26178) videosdk: using 1 STUN server(s) for ICE
I (29220) peer: Created inbound SRTP session
I (29220) peer: Created outbound SRTP session
I (29474) videosdk: DataChannel transport up (ICE+DTLS)
I (29534) videosdk: SCTP data channel connected after 60 ms
I (29807) videosdk: audio RTP producer id=4bb13f4c-b453-4445-8924-ab09215dfd1b
I (29807) videosdk: bringing up mic
Result:0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The two lines that matter are &lt;code&gt;Created outbound SRTP session&lt;/code&gt; and the &lt;code&gt;RTP producer id&lt;/code&gt;. Together they confirm the DTLS handshake completed and the board is publishing. A few &lt;code&gt;example_connect: Wi-Fi disconnected, trying to reconnect...&lt;/code&gt; lines before the association are normal on a busy 2.4 GHz channel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 7: Join the Same Room From a React App
&lt;/h2&gt;

&lt;p&gt;Nothing about the web side is ESP32-specific, which is the point: the board is just another participant. VideoSDK publishes a companion repository, &lt;a href="https://github.com/videosdk-live/videosdk-rtc-iot-sdk-example" rel="noopener noreferrer"&gt;videosdk-live/videosdk-rtc-iot-sdk-example&lt;/a&gt;, that carries the firmware under &lt;code&gt;IoT/&lt;/code&gt; and two ready-to-run browser clients under &lt;code&gt;web/js&lt;/code&gt; (vanilla JavaScript) and &lt;code&gt;web/react&lt;/code&gt;. Both talk to the same room, so pick whichever matches your stack. This guide uses the React client.&lt;/p&gt;

&lt;p&gt;Clone the repository and install the client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/videosdk-live/videosdk-rtc-iot-sdk-example.git
&lt;span class="nb"&gt;cd &lt;/span&gt;videosdk-rtc-iot-sdk-example/web/react
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the environment file and drop in the token from Step 2:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REACT_APP_VIDEOSDK_TOKEN=&amp;lt;your token&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The token must carry the &lt;code&gt;allow_join&lt;/code&gt; permission. Create React App inlines &lt;code&gt;.env&lt;/code&gt; at build time, so restart the dev server after any change to it, otherwise you will keep authenticating with the old value and blame the board.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The app comes up on &lt;code&gt;http://localhost:3000&lt;/code&gt;. Browsers only grant camera and microphone access on a secure context, and &lt;code&gt;localhost&lt;/code&gt; counts as one, so no TLS setup is needed for local development.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3lo0rm2ay3bpdhjdva49.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3lo0rm2ay3bpdhjdva49.png" alt="Joining Screen" width="800" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;New Meeting&lt;/strong&gt;. The app creates the room and shows you the meeting ID. That ID goes into &lt;code&gt;idf.py menuconfig&lt;/code&gt; under &lt;strong&gt;VideoSDK Configuration &amp;gt; Meeting / room ID&lt;/strong&gt; back in Step 4. Reflash the board, and once it joins, the ESP32-S3 appears as its own video tile under the &lt;code&gt;displayName&lt;/code&gt; you set in Step 5.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdfnfyr2r8dr1w0rtzv2u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdfnfyr2r8dr1w0rtzv2u.png" alt="Meeting UI" width="800" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What the React Client Adds for IoT Participants
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;MeetingView.js&lt;/code&gt; is an ordinary VideoSDK meeting view built on the same hooks as the &lt;a href="https://docs.videosdk.live/react/guide/video-and-audio-calling-api-sdk/quick-start" rel="noopener noreferrer"&gt;React SDK quickstart&lt;/a&gt;, with no device-specific logic in it. Everything that knows about hardware lives in &lt;code&gt;src/iot&lt;/code&gt;, which is a useful separation to copy if you are bolting device support onto an app you already have.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Export&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;IOTBridge /&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The one piece that has to be mounted for device support to work&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;IOTVideoPlayer participantId={id} /&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Renders a device participant's video and audio&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;useIOTMessages()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hook for receiving messages sent from the board's data channel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sendIOTMessage()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sends a message to the device, 15 KiB maximum per message&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;isIOTDevice()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tells a device participant apart from a browser participant&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;useIOTMessages()&lt;/code&gt; and &lt;code&gt;sendIOTMessage()&lt;/code&gt; are the browser half of the &lt;code&gt;setDataMessageHandler()&lt;/code&gt; and &lt;code&gt;sendMessage()&lt;/code&gt; calls from Step 5. That pairing is what turns a call into a control channel: stream the camera for the human, and send a JSON command over the same connection to move a servo or read a sensor, with no second transport to operate.&lt;/p&gt;

&lt;p&gt;On a Korvo-2, this is also where the loop closes. Your browser webcam draws to the board's LCD and your voice plays through its speaker, while its camera and microphone come back to you in the tile.&lt;/p&gt;

&lt;h3&gt;
  
  
  Common Errors
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;DEVICE_NOT_SUPPORTED&lt;/code&gt; on a subscribe call:&lt;/strong&gt; Expected on boards without LCD or speaker output, such as the XIAO. Use a Korvo-2 for playback on the device.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;INIT_NOT_CALLED&lt;/code&gt;:&lt;/strong&gt; A &lt;code&gt;start*&lt;/code&gt; function ran before &lt;code&gt;init()&lt;/code&gt; returned &lt;code&gt;RESULT_OK&lt;/code&gt;. Check the &lt;code&gt;init()&lt;/code&gt; return code before starting streams.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build fails or the image will not fit:&lt;/strong&gt; Confirm ESP-IDF is on v5.4 or newer, flash size is set to 8 MB, and PSRAM is enabled for the selected board.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wi-Fi connects but the room join fails:&lt;/strong&gt; Verify the room ID formatting and confirm the token carries &lt;code&gt;allow_join&lt;/code&gt; permission and has not expired.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Board joins but no media arrives:&lt;/strong&gt; Confirm the browser used the same room ID, and check the serial log for &lt;code&gt;Created outbound SRTP session&lt;/code&gt;. Its absence points at the DTLS handshake, usually a blocked UDP path on the network.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ESP32 Video Calling Glossary
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Room:&lt;/strong&gt; A VideoSDK meeting space identified by a unique room ID that participants join to share media streams. The ESP32-S3 and your browser join the same room ID, which is what makes them see each other.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Participant:&lt;/strong&gt; Any client connected to a room with its own audio and video streams. The IoT SDK registers the board as a participant with a &lt;code&gt;displayName&lt;/code&gt; and a &lt;code&gt;participantId&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Meeting Token:&lt;/strong&gt; A JWT authorizing a participant to join a room, generated from the VideoSDK dashboard for testing or minted server-side for production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SRTP:&lt;/strong&gt; Secure Real-time Transport Protocol, the encrypted media transport WebRTC uses. The IoT SDK negotiates SRTP keys through a DTLS handshake, so board media is encrypted on the wire exactly as browser media is.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ESP-IDF component:&lt;/strong&gt; A reusable package in Espressif's IoT Development Framework, resolvable from the component registry. The VideoSDK IoT SDK ships as one, so &lt;code&gt;idf.py&lt;/code&gt; pulls it in as a dependency rather than you vendoring source.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Can an ESP32 join real-time calls?
&lt;/h3&gt;

&lt;p&gt;Yes. An ESP32-S3 running the VideoSDK IoT SDK connects directly to a VideoSDK room over WebRTC and streams video and audio with browsers, phones, and other devices in real time. The board is a full participant in the room, not a feed proxied through a gateway.&lt;/p&gt;

&lt;h3&gt;
  
  
  Which ESP32 boards are supported?
&lt;/h3&gt;

&lt;p&gt;The IoT SDK targets the ESP32-S3 family. The XIAO ESP32-S3 (Sense) handles camera and microphone transmission, while the ESP32-S3-Korvo-2 v3.0 supports full two-way A/V with its integrated LCD and speaker driver.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does the IoT SDK use standard WebRTC?
&lt;/h3&gt;

&lt;p&gt;Yes. The VideoSDK IoT SDK uses standard WebRTC protocols end to end, establishing encrypted SRTP media channels through DTLS handshakes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can two ESP32 devices call each other?
&lt;/h3&gt;

&lt;p&gt;Yes. Two-way interactive calling works when both ESP32 devices have media output hardware, such as two ESP32-S3-Korvo-2 boards equipped with speakers and displays. If a send-only board like the XIAO ESP32-S3 is used, it can publish its streams into the room but cannot receive or play back incoming media.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why does the ESP32 send JPEG instead of H.264?
&lt;/h3&gt;

&lt;p&gt;JPEG compresses each frame independently, so it needs no reference frames and far less RAM than a video codec. On the supported boards the camera sensor emits JPEG directly, which keeps the ESP32-S3 out of the encoding path entirely.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is the IoT SDK free to use?
&lt;/h3&gt;

&lt;p&gt;The IoT SDK component is open source under the MIT license. VideoSDK provides a free monthly credit tier covering room connectivity and media routing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;ESP32 video calling stops being an infrastructure project once the board can join a managed room directly. The IoT SDK reduces it to a token, a room ID, and four function calls, and the browser side is a repository you clone. The remaining engineering is where it should be: your product's camera placement, power budget, and reconnect behavior.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The example repo:&lt;/strong&gt; &lt;a href="https://github.com/videosdk-live/videosdk-rtc-iot-sdk-example" rel="noopener noreferrer"&gt;videosdk-live/videosdk-rtc-iot-sdk-example&lt;/a&gt;, firmware plus React and JavaScript clients&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Free API keys:&lt;/strong&gt; &lt;a href="https://dub.sh/X5Fn46e?utm_source=esp32-video-calling-videosdk-iot-sdk" rel="noopener noreferrer"&gt;VideoSDK dashboard&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The component:&lt;/strong&gt; &lt;a href="https://components.espressif.com/components/videosdk/iot-sdk" rel="noopener noreferrer"&gt;videosdk/iot-sdk on the Espressif registry&lt;/a&gt;, MIT licensed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Flash it once and watching a microcontroller appear in a browser participant list is genuinely strange in a good way. What are you building with VideoSDK? Drop a comment, I would love to hear what kind of embedded calling use case you are working on.&lt;/p&gt;

</description>
      <category>webrtc</category>
      <category>esp32</category>
      <category>tutorial</category>
      <category>learning</category>
    </item>
    <item>
      <title>How to Build an Open-Source Zoom Clone with Claude Code + VideoSDK</title>
      <dc:creator>Zishan Ghaniwala</dc:creator>
      <pubDate>Mon, 27 Jul 2026 13:27:53 +0000</pubDate>
      <link>https://dev.to/zishan_vsdk/how-to-build-an-open-source-zoom-clone-with-claude-code-videosdk-4im9</link>
      <guid>https://dev.to/zishan_vsdk/how-to-build-an-open-source-zoom-clone-with-claude-code-videosdk-4im9</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;To build a Google Meet clone, clone the MIT-licensed &lt;code&gt;videosdk-community/google-meet-clone&lt;/code&gt; repo, run &lt;code&gt;npm install&lt;/code&gt; and &lt;code&gt;npm run dev&lt;/code&gt;, then add a free &lt;code&gt;VIDEOSDK_API_KEY&lt;/code&gt; and &lt;code&gt;VIDEOSDK_SECRET&lt;/code&gt; to &lt;code&gt;.env&lt;/code&gt; for real WebRTC video over VideoSDK's SFU. Claude Code handles the customizing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is the whole path, and it takes about ten minutes. You start from a complete React video call app: a pixel-accurate Google Meet UI sitting on top of VideoSDK's real WebRTC infrastructure. You clone it, you run it, you make it yours. No real-time video experience needed, no signaling server to write, no TURN infrastructure to rent.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffb9ldjeg9fdhkwyys0bi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffb9ldjeg9fdhkwyys0bi.png" alt="Meeting UI" width="800" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Takeaways
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The repo is free and MIT-licensed.&lt;/strong&gt; Clone &lt;code&gt;videosdk-community/google-meet-clone&lt;/code&gt;, run &lt;code&gt;npm install&lt;/code&gt;, and you have the full codebase.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Preview the UI with zero keys.&lt;/strong&gt; A built-in fake mode renders the interface locally so you can click around before signing up for anything.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Two keys unlock real video.&lt;/strong&gt; A free &lt;code&gt;VIDEOSDK_API_KEY&lt;/code&gt; and &lt;code&gt;VIDEOSDK_SECRET&lt;/code&gt; turn it into genuine multi-participant meetings. The server mints tokens for you.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Claude Code does the customizing.&lt;/strong&gt; Ask it to rebrand, add features, or change behavior in plain English.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One click to deploy.&lt;/strong&gt; A Vercel button ships it to the internet with the two env vars pre-filled.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is a Google Meet clone?
&lt;/h2&gt;

&lt;p&gt;A Google Meet clone is defined as a self-hosted video conferencing app that reproduces Google Meet's interface and meeting mechanics on infrastructure you control. It works by pairing a Meet-styled front end with a WebRTC media backend that routes audio and video between participants.&lt;/p&gt;

&lt;p&gt;The hard part was never the UI. It is the media layer: peer negotiation, an SFU to route streams so a five-person call does not melt a laptop, TURN relays for participants behind restrictive firewalls, and signed tokens so a guest cannot grant themselves host powers.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;videosdk-community/google-meet-clone&lt;/code&gt; repo hands you the UI and delegates that media layer to VideoSDK's SFU, which is why the whole thing fits in one afternoon instead of one quarter. Real-time transport follows the &lt;a href="https://www.w3.org/TR/webrtc/" rel="noopener noreferrer"&gt;W3C WebRTC specification&lt;/a&gt;, so the browser side is standards-based rather than proprietary.&lt;/p&gt;

&lt;p&gt;Most of the competing tutorials in this space stop before that line. The &lt;a href="https://strapi.io/blog/build-a-google-meet-clone-with-strapi5-and-nextjs-part-1" rel="noopener noreferrer"&gt;Strapi 5 and Next.js series&lt;/a&gt; and the popular WebRTC-plus-Socket.io courses have you hand-roll signaling with peer-to-peer connections, which works for two people and degrades badly past four. This guide starts from an SFU, so scaling is not a rewrite.&lt;/p&gt;

&lt;h2&gt;
  
  
  Features the repo ships with
&lt;/h2&gt;

&lt;p&gt;The repo ships the feature set that actually matters in a meeting app, not a video-chat demo with two tiles.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Real-time HD video and audio over VideoSDK's SFU, so calls scale past two people.&lt;/li&gt;
&lt;li&gt;A grid / gallery view with tiles that resize themselves as the room fills up.&lt;/li&gt;
&lt;li&gt;A pre-join green room with a mirrored camera preview and device pickers.&lt;/li&gt;
&lt;li&gt;Chat with saved history, floating emoji reactions, and raise hand, all synced to late joiners.&lt;/li&gt;
&lt;li&gt;Screen sharing and cloud recording.&lt;/li&gt;
&lt;li&gt;Host controls with a real waiting room: guests knock, and the host admits or removes them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It looks like Google Meet because the UI is built from a Figma reference, down to the icons and fonts. It works like Google Meet because the media is real.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fihv8vvob9jtfhz8dsr1b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fihv8vvob9jtfhz8dsr1b.png" alt="Landing page of the open-source Google Meet clone with a New meeting button and a join-by-code field" width="800" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites for building the clone
&lt;/h2&gt;

&lt;p&gt;You need very little to start:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Node.js 18 or newer&lt;/strong&gt; (20+ recommended). Check with &lt;code&gt;node -v&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;git&lt;/strong&gt;, to clone the repo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A free VideoSDK account&lt;/strong&gt; for live video. You'll grab the keys in Step 3, so you can skip this until then.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Claude Code&lt;/strong&gt; (optional, but it's the star of Step 4). It's Anthropic's terminal coding agent, documented at &lt;a href="https://docs.claude.com/en/docs/claude-code/overview" rel="noopener noreferrer"&gt;docs.claude.com&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's it. No database, no backend to stand up, no accounts system to wire in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1 - Clone the Google Meet clone repo
&lt;/h2&gt;

&lt;p&gt;Getting the code is two commands and one install, and the lockfile pins every dependency for you.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/videosdk-community/google-meet-clone
&lt;span class="nb"&gt;cd &lt;/span&gt;google-meet-clone
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This project uses &lt;strong&gt;npm&lt;/strong&gt; as its package manager, so &lt;code&gt;npm install&lt;/code&gt; reads the committed lockfile and pulls everything down: React 19, TypeScript, Vite, and the &lt;code&gt;@videosdk.live/react-sdk&lt;/code&gt;. Give it a minute. &lt;/p&gt;

&lt;p&gt;While that runs, here's the shape of what you cloned. Feature components live under &lt;code&gt;src/routes/&lt;/code&gt; (the landing page, the green room, and the in-call screen). Every call into VideoSDK is hidden behind a single hook layer in &lt;code&gt;src/meeting/&lt;/code&gt;, and the token-signing serverless functions sit in &lt;code&gt;api/&lt;/code&gt;. You rarely need to touch the SDK directly, which is exactly why customizing this is pleasant. If you do want the raw surface, it is the same one documented in the &lt;a href="https://docs.videosdk.live/react/guide/video-and-audio-calling-api-sdk/quick-start" rel="noopener noreferrer"&gt;VideoSDK React quickstart&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 - Preview the UI in fake mode
&lt;/h2&gt;

&lt;p&gt;You don't need any keys to see the interface. The project ships a &lt;strong&gt;fake mode&lt;/strong&gt; that renders the full UI against a local mock, no camera and no network required.&lt;/p&gt;

&lt;p&gt;Start the dev server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It serves the app on &lt;code&gt;http://localhost:5173&lt;/code&gt;. Now open a meeting URL with the &lt;code&gt;?fake=ok&lt;/code&gt; switch appended:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:5173/abc-defg-hij?fake=ok
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll first land in the green room, then join into a fully rendered meeting: participant tiles, the control bar, chat, reactions, the works. It's perfect for exploring the layout, tweaking styles, and getting your bearings.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flfj4jyjpvsxw3ge2weod.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flfj4jyjpvsxw3ge2weod.png" alt="Pre-join green room with a camera preview, a name field, device pickers, and a Join now button" width="800" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One honest caveat: &lt;strong&gt;fake mode is a UI preview, not a real call.&lt;/strong&gt; It uses a built-in fake client with fabricated participants, so nothing goes over the network and no one else can join. It's a developer convenience that's compiled out of production builds entirely. For actual meetings with real people, you need keys, which is the next step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3 - Add real video with VideoSDK keys
&lt;/h2&gt;

&lt;p&gt;Two environment variables turn the UI preview into genuine multi-participant meetings, and neither one ever reaches the browser.&lt;/p&gt;

&lt;p&gt;Head to the &lt;a href="https://dub.sh/X5Fn46e?utm_source=devto_blog&amp;amp;utm_medium=blog&amp;amp;utm_campaign=google_meet_clone" rel="noopener noreferrer"&gt;VideoSDK dashboard&lt;/a&gt; and create a free account. You'll get an &lt;strong&gt;API key&lt;/strong&gt; and a &lt;strong&gt;secret&lt;/strong&gt;. &lt;code&gt;[UPDATE: verify free tier limits - videosdk.live/pricing]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Copy the example env file and paste your credentials in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the new &lt;code&gt;.env&lt;/code&gt; and fill in the two values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VIDEOSDK_API_KEY=your_key
VIDEOSDK_SECRET=your_secret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those are the only two variables that matter. Restart the dev server (&lt;code&gt;npm run dev&lt;/code&gt;) and you're live. Click &lt;strong&gt;New meeting&lt;/strong&gt; on the landing page and it creates a real VideoSDK room and makes you its host. Open the meeting link in a second browser or device to join as a guest.&lt;/p&gt;

&lt;p&gt;Two things are worth knowing about how this stays safe. First, &lt;strong&gt;the secret never reaches the browser.&lt;/strong&gt; It's read only by the functions in &lt;code&gt;api/&lt;/code&gt;, and the project deliberately keeps it out of any &lt;code&gt;VITE_&lt;/code&gt;-prefixed variable so it can't get bundled into the client.&lt;/p&gt;

&lt;p&gt;Second, &lt;strong&gt;there's no manual token step.&lt;/strong&gt; When a client joins, the server mints the right meeting token on the spot: a host token if the client holds a valid grant for that room, a guest token otherwise. You paste your key and secret once, and token signing is automatic from then on. The token format and its permission claims are described in the &lt;a href="https://docs.videosdk.live/api-reference/realtime-communication/intro" rel="noopener noreferrer"&gt;VideoSDK authentication docs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4 - Customize the clone with Claude Code
&lt;/h2&gt;

&lt;p&gt;Claude Code turns customization into a conversation, because every VideoSDK call in this repo already sits behind one hook layer.&lt;/p&gt;

&lt;p&gt;Instead of hunting through files, you describe what you want and let the agent make the change. Run &lt;code&gt;claude&lt;/code&gt; in the project root and try prompts like these. (These are illustrative examples of what you'd ask, not magic incantations. Phrase them however feels natural.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rebrand the colors:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The app uses design tokens generated from Figma. Change the primary accent color to a deep purple across the UI, and show me which files you touched."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Add a reaction:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The reactions picker in the control bar has a set of emoji. Add a fire emoji to the list so participants can react with it, and make sure it floats up on the tiles like the others."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Tweak the meeting controls:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"In the in-call control bar, add a keyboard shortcut that toggles the People panel, and show it in the button's tooltip."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Understand before you change:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Explain how a guest goes from clicking a meeting link to appearing in the host's waiting room. Trace the path through the code."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Point Claude Code at &lt;code&gt;src/routes/&lt;/code&gt; for anything visual and &lt;code&gt;src/meeting/&lt;/code&gt; for anything media-related, and it'll follow the existing patterns rather than reinventing them. Ask, review the diff, run the app, repeat. That loop is the whole workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5 - Deploy to Vercel
&lt;/h2&gt;

&lt;p&gt;Deployment is one button because the repo ships nothing that needs configuring, only the two secrets.&lt;/p&gt;

&lt;p&gt;When you're ready to share it, the fastest path is the one-click Vercel button in the repo's README. It clones the project into your own Vercel account and prompts you for the two environment variables (&lt;code&gt;VIDEOSDK_API_KEY&lt;/code&gt; and &lt;code&gt;VIDEOSDK_SECRET&lt;/code&gt;), with a link straight to your API keys.&lt;/p&gt;

&lt;p&gt;Prefer to do it yourself? Build and deploy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then deploy to any host that runs the &lt;code&gt;api/&lt;/code&gt; serverless functions. On Vercel it's zero-config: there's no &lt;code&gt;vercel.json&lt;/code&gt; to write, the functions become endpoints automatically, and the single-page app is served statically. Set the same two env vars in your host's dashboard, and tokens get minted at request time. Nothing is baked into the client, and there's nothing to paste per meeting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tour of the running meeting app
&lt;/h2&gt;

&lt;p&gt;Three surfaces make up the whole app: a landing page, a green room, and the call itself.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;landing page&lt;/strong&gt; (&lt;code&gt;/&lt;/code&gt;) is where you start a meeting or join by code. Click &lt;strong&gt;New meeting&lt;/strong&gt; and it spins up a real room and hands you the host role.&lt;/p&gt;

&lt;p&gt;Every meeting lives at &lt;code&gt;/:meetingId&lt;/code&gt;, and that one route covers both stages. First you hit the &lt;strong&gt;green room&lt;/strong&gt;: type your name, check yourself in the mirrored camera preview, pick your mic and camera, and hit &lt;strong&gt;Join now&lt;/strong&gt;. Then the view swaps in place to the &lt;strong&gt;call itself&lt;/strong&gt;, and the URL never changes, exactly like real Meet.&lt;/p&gt;

&lt;p&gt;Inside the call you get the grid of participant tiles that size themselves to the room, a control bar for mic, camera, screen share, reactions, and raise hand, plus a chat panel that keeps its history so late joiners catch up.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh4wato7ttgh2h24g8o7i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh4wato7ttgh2h24g8o7i.png" alt="In-call view with the chat panel open, showing saved message history from earlier participants" width="800" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Open the &lt;strong&gt;People&lt;/strong&gt; panel to see who's in, and if you're the host, you get the controls that make it real: a waiting room where guests knock for admission, the ability to mute at the media server, and cloud recording with a live indicator. Host power isn't a hidden button either. It's a server-signed grant, so a guest's client simply doesn't hold the capability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Taking your Google Meet clone to production
&lt;/h2&gt;

&lt;p&gt;Five things change between &lt;code&gt;localhost:5173&lt;/code&gt; and a meeting your users trust, and most clone tutorials never mention any of them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HTTPS is not optional.&lt;/strong&gt; Browsers only expose &lt;code&gt;getUserMedia&lt;/code&gt; on secure origins, with &lt;code&gt;localhost&lt;/code&gt; as the single exemption. Deploy behind TLS or the camera never turns on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NAT traversal is already handled, and that is the point.&lt;/strong&gt; Roughly one in five WebRTC connections cannot be established peer-to-peer and needs a TURN relay. Because media here goes through VideoSDK's SFU, you inherit its ICE and relay infrastructure instead of standing up and paying for &lt;code&gt;coturn&lt;/code&gt;. &lt;code&gt;[UPDATE: verify current TURN fallback share against VideoSDK network reports]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Token lifetime needs a decision.&lt;/strong&gt; The functions in &lt;code&gt;api/&lt;/code&gt; mint short-lived meeting tokens per join. If you extend the app with long meetings or reconnect flows, decide what happens when a token expires mid-call, and refresh rather than dropping the participant.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recordings need a home.&lt;/strong&gt; Cloud recording writes to VideoSDK storage by default. For anything with a retention or residency requirement, wire the recording webhook to your own bucket and record where that data may legally live.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SPA fallback breaks deep links if you forget it.&lt;/strong&gt; Meeting URLs are client-side routes. On Vercel this is automatic; on Nginx, S3, or any static host, add a catch-all rewrite to &lt;code&gt;index.html&lt;/code&gt; or &lt;code&gt;/:meetingId&lt;/code&gt; returns a 404 to every invited guest.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does this Google Meet clone compare to Google Meet?
&lt;/h2&gt;

&lt;p&gt;This clone matches Google Meet's UI and meeting mechanics, and deliberately skips the accounts-and-history backend behind it.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;This clone&lt;/th&gt;
&lt;th&gt;Google Meet&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;HD video, audio, screen share&lt;/td&gt;
&lt;td&gt;Yes, over VideoSDK's SFU&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chat, reactions, raise hand&lt;/td&gt;
&lt;td&gt;Yes, synced to late joiners&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Waiting room and host controls&lt;/td&gt;
&lt;td&gt;Yes, server-signed grants&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloud recording&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes, on paid Workspace tiers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Identity and accounts&lt;/td&gt;
&lt;td&gt;A name you type&lt;/td&gt;
&lt;td&gt;Google account&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Calendar and meeting history&lt;/td&gt;
&lt;td&gt;Not included&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source code and self-hosting&lt;/td&gt;
&lt;td&gt;MIT, fully yours&lt;/td&gt;
&lt;td&gt;Closed, hosted only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The verdict is conditional. Use Google Meet when you want meetings to just exist for a team that already lives in Google Workspace. Build on this clone when video needs to sit inside your own product, carry your branding, and follow your own permission model. It is a starting point for a product, not a replacement for a hosted meeting service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Definitions glossary for meeting apps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Room&lt;/strong&gt; - the VideoSDK unit a meeting happens in. Participants join a room by ID, and every media stream is scoped to it. Here, the room ID is the &lt;code&gt;/:meetingId&lt;/code&gt; in the URL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Participant&lt;/strong&gt; - one connected client in a room, carrying its own audio, video, and screen-share tracks plus a role that decides what it may do.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Meeting token&lt;/strong&gt; - a short-lived signed credential granting a client permission to join a specific room, minted server-side in &lt;code&gt;api/&lt;/code&gt; from your API key and secret.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SFU (Selective Forwarding Unit)&lt;/strong&gt; - a media server that receives each participant's stream once and forwards it to the others, so upload cost stays flat as the room grows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prebuilt UI Kit&lt;/strong&gt; - VideoSDK's drop-in meeting interface, embeddable as an iframe or React component when you want a working call without building the UI at all.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Frequently asked questions about Google Meet clones
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Do I need a VideoSDK account to build a Google Meet clone with this repo?&lt;/strong&gt;&lt;br&gt;
For real meetings, yes, and it's free. You need a &lt;code&gt;VIDEOSDK_API_KEY&lt;/code&gt; and &lt;code&gt;VIDEOSDK_SECRET&lt;/code&gt; from the &lt;a href="https://dub.sh/X5Fn46e?utm_source=devto_blog&amp;amp;utm_medium=blog&amp;amp;utm_campaign=google_meet_clone" rel="noopener noreferrer"&gt;dashboard&lt;/a&gt;. To just explore the UI, no: fake mode (&lt;code&gt;?fake=ok&lt;/code&gt;) runs with no keys at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is the keyless run a real call?&lt;/strong&gt;&lt;br&gt;
No, the keyless run is not a real call. Fake mode is a local UI preview using a built-in mock client. It's great for design work and clicking through the interface, but nobody can actually join and nothing goes over the network. Add the two keys for genuine multi-participant video.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is this Google Meet clone really free and open source?&lt;/strong&gt;&lt;br&gt;
Yes, it is genuinely free and open source. The repo is released under the &lt;a href="https://opensource.org/license/mit" rel="noopener noreferrer"&gt;MIT License&lt;/a&gt;, so you're free to use, modify, self-host, and build a commercial product on top of it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I self-host it instead of using Vercel?&lt;/strong&gt;&lt;br&gt;
Yes, you can self-host it anywhere that runs Node functions. Set &lt;code&gt;VIDEOSDK_API_KEY&lt;/code&gt; and &lt;code&gt;VIDEOSDK_SECRET&lt;/code&gt; as environment variables, run &lt;code&gt;npm run build&lt;/code&gt;, and deploy the &lt;code&gt;api/&lt;/code&gt; functions alongside the static build. On static-only hosts, add an SPA fallback so meeting deep-links load instead of 404ing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I have to write token-minting code?&lt;/strong&gt;&lt;br&gt;
No, you do not write any token-minting code. The server mints host and guest tokens per request from your key and secret. There's nothing to sign by hand and no token script to run.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How many participants can a meeting hold?&lt;/strong&gt;&lt;br&gt;
Capacity is set by VideoSDK's SFU rather than the UI, so the grid layout is the practical limit long before the media layer is. Check your plan's concurrency limits before promising a number. &lt;code&gt;[UPDATE: verify participant limits - videosdk.live/pricing]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I build a Zoom clone the same way?&lt;/strong&gt;&lt;br&gt;
Yes, the same architecture applies. Swap the Meet-styled components in &lt;code&gt;src/routes/&lt;/code&gt; for your own layout and keep the &lt;code&gt;src/meeting/&lt;/code&gt; hook layer untouched, since the media logic is UI-agnostic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion and next steps
&lt;/h2&gt;

&lt;p&gt;You're a few commands away from your own open-source Google Meet clone, running on real WebRTC infrastructure rather than a demo signaling server.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Try it live&lt;/strong&gt; on &lt;a href="https://google-meet-clone-by-videosdk.vercel.app/" rel="noopener noreferrer"&gt;google-meet-clone-by-videosdk.vercel.app&lt;/a&gt;, the deployed demo you can open right now.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fork&lt;/strong&gt; &lt;code&gt;videosdk-community/google-meet-clone&lt;/code&gt; and &lt;code&gt;npm install&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Grab free keys&lt;/strong&gt; from the &lt;a href="https://dub.sh/X5Fn46e?utm_source=devto_blog&amp;amp;utm_medium=blog&amp;amp;utm_campaign=google_meet_clone" rel="noopener noreferrer"&gt;VideoSDK dashboard&lt;/a&gt; and drop them in &lt;code&gt;.env&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open Claude Code&lt;/strong&gt; and start shaping it into your product.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;From there, the natural next steps are the &lt;a href="https://docs.videosdk.live/react/guide/video-and-audio-calling-api-sdk/quick-start" rel="noopener noreferrer"&gt;VideoSDK React quickstart&lt;/a&gt; if you want to understand the hook layer, the &lt;a href="https://docs.videosdk.live/prebuilt/guide/prebuilt-video-and-audio-calling/quick-start" rel="noopener noreferrer"&gt;Prebuilt UI Kit&lt;/a&gt; if you'd rather skip UI work entirely on your next app, and &lt;a href="https://docs.videosdk.live/react/guide/interactive-live-streaming/quick-start" rel="noopener noreferrer"&gt;interactive live streaming&lt;/a&gt; if your product needs an audience larger than a meeting.&lt;/p&gt;

&lt;p&gt;Real video, a polished UI, and an AI agent to do the heavy lifting. What are you building on top of it?&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>claude</category>
      <category>beginners</category>
      <category>react</category>
    </item>
  </channel>
</rss>
