DEV Community

Cover image for How to Add Multiplayer to Your Discord Activity
WavePlay Staff for WavePlay

Posted on

How to Add Multiplayer to Your Discord Activity

Discord Activities are web apps that run inside Discord, typically in the form of games or other interactive content using Discord's Embedded App SDK. They're currently in preview, but you can start building them today!

In this guide, we'll create a video player that everyone in a channel can control as a Discord Activity using Robo.js and React. Let's get started!

Creating the Activity Project

First, you'll need to have Node.js installed and an editor like VS Code to get started. Next, create a new Discord Activity project using the following command in your terminal:



npx create-robo <projectName> -k activity


Enter fullscreen mode Exit fullscreen mode

This will spawn a new Discord Activity project ready to use and made easier with the Robo.js framework. For this guide, we'll be using TypeScript and React. We've named our project teamplay.

Image

If this is your first time making a Discord Activity, you'll need to create an app in the Discord Developer Portal and get your Client ID and Client Secret.

Need more help? Check out Creating a Discord Activity in seconds with Robo.js

Running the Activity

Now that our project is set up, open it in your code editor and run the following command in your terminal:



npm run dev


Enter fullscreen mode Exit fullscreen mode

This will start the Robo project in development mode, refreshing with every change in code. It will also create a free Cloudflare tunnel for testing.

Image

Copy the tunnel URL and paste it into your Discord App's URL Mappings in the Discord Developer Portal. This will allow Discord to communicate with it.

Image

With the project running, join a voice channel in your test Discord server and click the rocket button to join a Discord Activity. You should see your activity running in Discord!

Image

Setting Up the Video Player

We need a video file to play. You can use any video file you like, but for this guide, we'll be using a sample video you can download from Coverr. Let's name it sample.mp4 and place it in the /public folder of your new project.

Image

With the video file in place, let's add a <video> element to our main page. Open the Activity.tsx file in the /src/app folder and replace the existing code with the following:



import { useEffect, useRef, useState } from 'react'

export const Activity = () => {
    const [isPlaying, setPlaying] = useState(false)
    const videoPlayer = useRef<HTMLVideoElement>(null)

    const onPause = () => {
        if (isPlaying) {
            setPlaying(false)
        }
    }
    const onPlay = () => {
        if (!isPlaying) {
            setPlaying(true)
        }
    }

    useEffect(() => {
        if (isPlaying) {
            videoPlayer.current?.play()
        } else if (!isPlaying) {
            videoPlayer.current?.pause()
        }
    }, [isPlaying])

    return (
        <div>
            <img src="/rocket.png" className="logo" alt="Discord" />
            <br />
            <video ref={videoPlayer} className="video" src="/sample.mp4" controls={false} loop />
            <br />
            <button onClick={isPlaying ? onPause : onPlay}>{isPlaying ? 'Pause' : 'Play'}</button>
        </div>
    )
}



Enter fullscreen mode Exit fullscreen mode

Let's also adjust the video player styling a bit. Add the following CSS to the App.css file in the /src/app folder:



video {
    width: 50%;
}


Enter fullscreen mode Exit fullscreen mode

Ta-dah! We now have a video player in our Discord Activity that can be controlled by a button. Let's add some multiplayer features to it!

Controling the Video

To allow everyone to control the video player, we'll use the @robojs/sync plugin to sync the video player state across all clients in real-time. There's many ways to add multiplayer features but this is the easiest.

Run the following in your terminal:



npx robo add @robojs/sync


Enter fullscreen mode Exit fullscreen mode

Then wrap the Activity component with SyncContextProvider in App.tsx:



import { SyncContextProvider } from '@robojs/sync'
import { DiscordContextProvider } from '../hooks/useDiscordSdk'
import { Activity } from './Activity'
import './App.css'

export default function App() {
    return (
        <DiscordContextProvider>
            <SyncContextProvider>
                <Activity />
            </SyncContextProvider>
        </DiscordContextProvider>
    )
}


Enter fullscreen mode Exit fullscreen mode

We now have a websocket-powered connection between clients! This would normally require a lot of code, but @robojs/sync automatically upgrades your web server to handle websockets and syncs the state for you.

If you're already familiar with React's useState hook, you'll feel right at home with useSyncState. You can use it just like React's useState hook, but the state will be synced across all clients in real-time. Just provide a dependency array that acts as a unique identifier for the state.

To make it multiplayer, replace the useState hook in Activity.tsx with useSyncState and provide a unique identifier for the state. In this case, we'll use the string 'video':



import { useSyncState } from '@robojs/sync'
import { useEffect, useRef } from 'react'

export const Activity = () => {
    const [isPlaying, setPlaying] = useSyncState(false, ['video'])
    // ... rest of the code remains the same
}


Enter fullscreen mode Exit fullscreen mode

Now, when you click the play button, everyone using your activity will see the video playing. And when you pause it, everyone will see it pause!

But wait, what if you want to sync state by channels? You can do that too! Just provide the channel ID as the second argument in useSyncState, which you can get from the Discord SDK.



import { useDiscordSdk } from '../hooks/useDiscordSdk'
import { useSyncState } from '@robojs/sync'
import { useEffect, useRef } from 'react'

export const Activity = () => {
    const { discordSdk } = useDiscordSdk()
    const [isPlaying, setPlaying] = useSyncState(false, ['video', discordSdk.channelId])
    // ... rest of the code remains the same
}


Enter fullscreen mode Exit fullscreen mode

Now each channel has their own state! Invite a friend to join your Discord Activity and see the video player in action.

Image

Wasn't That Easy?

With Robo.js and @robojs/sync, adding multiplayer features to your Discord Activity is a breeze. You can now create interactive experiences that everyone in a channel can enjoy together! You can also add more features like volume control, seeking, and even mouse tracking using useSyncState! πŸš€

What's more, you can use the built-in Flashcore Database to persist data easily or Explore Plugins to add new features with one command, such as AI Chatbot Features or Web Servers. You can even create Discord Bots with it! If you're looking for something more powerful, we also have a Colyseus Discord Activity Template for real-time multiplayer games.

Don't forget to join our Discord server to chat with other developers, ask questions, and share your projects. We're here to help you build amazing apps with Robo.js! πŸš€

➞ πŸš€ Community: Join our Discord Server

Our very own Robo, Sage, is there to answer any questions about Robo.js, Discord.js, and more

Top comments (4)

Collapse
 
thomasbnt profile image
Thomas Bnt

Awesome!

Collapse
 
waveplay-staff profile image
WavePlay Staff

Thanks, we had some fun making this! ✨

Collapse
 
mickhence profile image
Mark henry

Awesome thanks for sharing

Collapse
 
nicmeister profile image
Nicolaas Nel

Pretty cool! Got any idea how this can be done without RoboJS?
I'm writing a pure HTML/JS project that' I'm trying to sync for all clients.