loading...
Cover image for Spook your colleagues with a Halloween themed virtual standup 👻🎃✨
Daily

Spook your colleagues with a Halloween themed virtual standup 👻🎃✨

kimberleejohnson profile image Kimberlee Johnson ・7 min read

Holidays like Halloween are shaping up to look a bit different this year, with most office celebrations going virtual. But have no fear, spooky season always finds a way!

At Daily, we power 1-click video for websites and apps. Developers use our tools to embed fully functional prebuilt video chat widgets in minutes, and to build completely custom call experiences. In this tutorial, we’ll use Daily’s prebuilt UI to play trick-or-treat in our next virtual standup.

Colleagues on video chat call with candy in the background

What we’ll build

We’re going to embed Daily’s video chat widget into a webpage. We’ll decorate the page using a little CSS, JavaScript, and a whole lot of gifs. We’ll also listen for Daily API events to set random background images and sound effects when participants join the call.

What you’ll need to build it

If you’d rather jump straight to a working demo, you’ll want to clone our daily-demos repository. After that:

cd daily-demos
cd static-demos
cd spooky-demo 
Enter fullscreen mode Exit fullscreen mode

In spooky.html, replace the Daily room URL placeholder with your own room URL. Same goes for the GIPHY API key placeholder, and then you’ll be good to npm run dev.

If you prefer to add a spooky call to your own website or app, read on for step-by-step instructions.

Hocus Pocus witches say just like this

How to build it

Create a Daily room and embed the room on your webpage

A Daily room URL is made up of your unique Daily domain followed by the room name. Each room URL points to a unique video chat location, and looks something like:

https://your-domain.daily.co/<room-name>/

You can create rooms either through a POST request to the /rooms Daily API endpoint, or from the Daily dashboard. Pick whichever method and name you’d like. I went for the dashboard and set the room name to spooky, of course.

Daily Dashboard screenshot of Create Room page

Once you have a room, you can use Daily’s prebuilt UI by embedding the room URL into any website or app, for example:

<html>
  <script src="https://unpkg.com/@daily-co/daily-js"></script>
  <body>
    <script>
      callFrame = window.DailyIframe.createFrame();
      callFrame.join({ url: 'https://your-team.daily.co/<room-name>' })
    </script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

In our demo, we also take advantage of the DailyIframe iframeStyle property to position our video chat before we call the .join method:

callFrame = window.DailyIframe.createFrame({
         iframeStyle: {
           border: 0,
           display: 'block',
           margin: 'auto',
           width: '50%',
           height: '100%',
         },
       });
Enter fullscreen mode Exit fullscreen mode

Note: While we’re hard-coding our room URLs for the purpose of this demo, in production code you’ll want to generate your meeting rooms dynamically server side, without revealing them on the frontend. Read about how to set up an instant server with Glitch.

Randomize the background image

I definitely spent more time deciding what images to use for the backdrop of the call than I spent integrating the Daily video chat widget. I went down a little bit of a rabbit hole, and ultimately wound up using images from Unsplash, pixabay, and Pexels. I also really appreciated the Halloween illustrations on Mixkit and Undraw. Choose whatever speaks to you, the world is your cauldron!

Witches stir a cauldron

Once you’ve picked your images, it’s time to turn to Daily events, and listen for whenever a local participant joins a call. Whenever the joined-meeting event fires, we’ll call a function to set the background image:

callFrame
         ...
         .on('joined-meeting', setBackground)
         ...
Enter fullscreen mode Exit fullscreen mode

Our setBackground function stores a list of our photos’ names in an array. It then uses the length of the array and the built-in JavaScript Math object to pick a random array index. Once the random index is selected, the function manipulates the DOM body property to set the page's background image.

async function setBackground(event) {
       try {
         const photos = ['candy.jpg', 'forest.jpg', 'jack-o-lanterns.jpg'];
         let randomIndex = Math.ceil(Math.random() * (photos.length - 1));
         document.body.style.backgroundImage = `url('/static-demos/spooky-demo/assets/${photos[randomIndex]}')`;
       } catch (error) {
         console.error(error);
       }
     }
Enter fullscreen mode Exit fullscreen mode

In spooky.css, we set the body’s background-size property to cover, so the image, like the spooky forest below, will cover the entire screen behind the call.

Screenshot video call ontop of forest background

Rotate gifs

If random festive backgrounds are the trick, then rotating random gifs are the treat. To add these to your call, first you’ll need your GIPHY API key.

Our getGifs function makes a request to the GIPHY API search endpoint, and passes in a search query for Halloween gifs that are rated pg. It saves the response, an object full of gifs, in the gifs variable, which it returns.

async function getGifs() {
        try {
          // TODO: replace the below with you giphy API key
          // In production, you'll need to store your key securely, vs. hard coding
          const token = 'YOUR-GIPHY-API-KEY';
          const giphyEndpoint = `https://api.giphy.com/v1/gifs/search?api_key=${token}&q=halloween&rating=pg`;
          const response = await fetch(giphyEndpoint);
          const gifs = await response.json();
          return gifs;
        } catch (error) {
          console.error(error);
        }
      }
Enter fullscreen mode Exit fullscreen mode

We’ll call getGifs() within our run function. We call run() as soon as the page loads. Within run(), every 20 seconds we extract a random gif's url from the gifs response object. We save the randomly selected url in a url variable. We can now set the src property of <img id='gifs'> to url.

const gifSearchResults = await getGifs();

        setInterval(() => {
          try {
            let url =
              gifSearchResults.data[Math.floor(Math.random() * 50)].images
                .original.url;
            document.getElementById('gifs').src = url;
          } catch (error) {
            console.error(error);
          }
        }, 20 * 1000);
Enter fullscreen mode Exit fullscreen mode

If you’re curious about how we styled our gifs, here’s how we made them rotate through a circular "moon" in the top-right corner:

#gifs {
   object-fit: contain;
 }

 #gif-wrapper {
   position: fixed;
   border-radius: 50%;
   border: 2px solid #e2dccd;
   background: black;
   width: 15vw;
   height: 25vh;
   top: 2%;
   right: 5%;
   display: flex;
   justify-content: space-evenly;
   align-items: center;
   overflow: hidden;
 }
Enter fullscreen mode Exit fullscreen mode

But you should feel free to style however you like! Much like costumes, no one CSS style suits everyone. Make it your own!

Add sound effects to Daily events

Much like picking a background image, landing on my sound effect of choice was tough. I wound up going with a clip of The Addams Family theme song, but there are lots of options out there for creaky doors, cackling witches, and anything else you can imagine. I just couldn’t say no to these faces.

Addams family snapping

Once you have your sound, save it in your project. I put mine in my /assets folder along with my background images. Then, add an <audio> tag to your HTML:

<audio src='assets/the-addams-family-intro-theme-song.mp3'></audio>  
Enter fullscreen mode Exit fullscreen mode

Just like we did whenever the local participant joins a call, we added an event listener to play a sound whenever another participant logs on. We listen for the participant-joined event.

callFrame
      ...
         .on('participant-joined', playSound)
      ...
Enter fullscreen mode Exit fullscreen mode

When that happens, playSound finds our <audio> element and calls the DOM play() method.

async function playSound(event) {
       try {
         const sound = document.querySelector('audio');
         sound.play();
       } catch (error) {
         console.error(error);
       }
     }
Enter fullscreen mode Exit fullscreen mode

What’s next

There’s so much more you can do to customize your call: experiment with different event listeners, styles, etc. You could even build an entirely custom call using the Daily call object, e.g. placing participants' video streams in the windows of a haunted house.

You can customize Daily calls for any spooky or special occasion. I got the idea for this post from all of the fun wedding send-offs and birthday surprises my colleagues have built (especially Mattie Ruth!). If you want to get in on all that fun and more, including the challenges of building video chat infrastructure, we’re hiring! Please send me a note if you’re interested in joining the team. I’m happy to help any way I can.

Team dressed up on video call for wedding send-off

Discussion

pic
Editor guide