DEV Community

Cover image for Creating and Broadcasting a "LoFi Radio" Station with Amazon IVS
Todd Sharp for AWS

Posted on

Creating and Broadcasting a "LoFi Radio" Station with Amazon IVS

This post might not fall into the "normal" ways that you'd use live streaming with Amazon Interactive Video Service (Amazon IVS), but it's a fun project to play around and learn with and it illustrates some unique features of the Web Broadcast SDK (notably the ability to stream from a source other than a traditional camera and microphone).

You may be familiar with the concept of "lofi radio" channels, but if not, they usually consist of some sort of animated character in a mostly static scene with a looped audio track containing lofi music. These live streams are popular among people looking for a non-distracting soundtrack to relax, study, or chat and make new friends. It doesn't fit the "traditional" user generated content (UGC) model of a broadcaster streaming a game or their webcam, but there is no arguing that these streams are hugely popular. For example, as of the time of publishing this article, the Lofi Girl channel on YouTube currently has 11.7 million subscribers and boasts nearly 1.5 billion total views.

Maybe it's time that someone built an entire live streaming UGC platform that gives users the ability to create their own custom lofi channels and broadcast them to their friends? That's a free idea - run with it!

I'm going to assume that you're new to Amazon IVS, so we'll walk through the process of creating your own lofi live stream from the beginning. If you're already familiar with Amazon IVS, feel free to jump past the parts that you're already comfortable with.

Sign Up for a Free AWS Account

Step one is to sign up for a free AWS account. New accounts will be eligible for 5 hours of basic input, 100 hours of SD output, and a generous amount of monthly chat messages for the first 12 months. This should be plenty of time to play around with Amazon IVS and learn all about how it works.

Creating an Amazon IVS Channel

I've blogged about how to get started creating an Amazon IVS channel, but the quickest way to create a one-off channel is via the AWS CLI (install docs). The command to create a channel is:

$ aws ivs create-channel --name lofi-demo --latency-mode LOW --type BASIC
Enter fullscreen mode Exit fullscreen mode

This will return some important information about your stream:

{
  "channel": {
    "arn": "arn:aws:ivs:us-east-1:[redacted]:channel/[redacted]",
    "authorized": false,
    "ingestEndpoint": "f99084460c35.global-contribute.live-video.net",
    "latencyMode": "LOW",
    "name": "lofi-demo",
    "playbackUrl": "https://f99084460c35.us-east-1.playback.live-video.net/api/video/v1/us-east-1.[redacted].channel.[redacted].m3u8",
    "recordingConfigurationArn": "",
    "tags": {},
    "type": "BASIC"
  },
  "streamKey": {
    "arn": "arn:aws:ivs:us-east-1:[redacted]:stream-key/[redacted]",
    "channelArn": "arn:aws:ivs:us-east-1:[redacted]:channel/[redacted]",
    "tags": {},
    "value": "sk_us-east-1[redacted]"
  }
}
Enter fullscreen mode Exit fullscreen mode

Keep this info handy, as we'll need the ingestEndpoint, playbackUrl, and streamKey in just a bit.

Broadcasting to the Lofi Channel with the Web Broadcast SDK

Now that we have a channel created, we can immediately start broadcasting to it. If you've done any live streaming, you're probably familiar with desktop streaming software (like OBS, Streamlabs Desktop, etc). Instead of using third-party software, we're going to write some code to broadcast our lofi stream directly from a browser.

Before We Code: You should have your own animation handy and an MP3 track. Copyright is a thing - please only utilize assets that you have obtained the proper licensing to use. If your animation is a GIF, you'll want to convert it to an MP4 file (there are plenty of tools available online for this).

The first step is to create an HTML page that includes the Web Broadcast SDK script, and contains a <canvas> element for the broadcast preview, a <video> element for the source animation, and a button to start the broadcast. The src of the <video> tag should point to your animation source and should have a width and height of 1px (we're going to preview the video on the <canvas> in just a bit, so no need for the source video to be visible).

<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Lofi Radio</title>
  <script src="https://web-broadcast.live-video.net/1.2.0/amazon-ivs-web-broadcast.js"></script>
</head>
<body>

  <canvas id="broadcast-preview"></canvas>

  <video id="src-video" 
    src="/video/lofi.mp4" 
    style="width: 1px; height: 1px;"
    muted loop></video>

  <button id="broadcast-btn">Broadcast</button>

</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Next, create an external JS file called lofi.js (don't forget to include it in your HTML above). Start off this file with a DOMContentLoaded listener and define an init() function to get things set up. In the init() function, we'll create an instance of the IVSBroadcastClient from the Web Broadcast SDK. We'll need to pass it the ingestEndpoint from our channel. We'll also create a click handler for the broadcast button that will call a toggleBroadcast() function that we'll define in just a bit.

const init = () => {
  window.broadcastClient = IVSBroadcastClient.create({
    streamConfig: IVSBroadcastClient.BASIC_FULL_HD_LANDSCAPE,
    ingestEndpoint: [Your ingestEndpoint],
  });
  document.getElementById('broadcast-btn').addEventListener('click', toggleBroadcast);
};
document.addEventListener('DOMContentLoaded', init);
Enter fullscreen mode Exit fullscreen mode

Note: To keep things simple, we're setting the broadcastClient into the window scope. In reality, you'll probably be using a framework so you'll usually avoid using the global window scope like this.

Next, we need to create our video stream and attach it to the broadcastClient. Modify the init() function as follows:

const init = () => {
  window.broadcastClient = IVSBroadcastClient.create({
    streamConfig: IVSBroadcastClient.BASIC_FULL_HD_LANDSCAPE,
    ingestEndpoint: [Your ingestEndpoint],
  });

  window.video = document.getElementById('src-video');
  window.broadcastClient.addImageSource(window.video, 'video-track', { index: 0 });
  const preview = document.getElementById('broadcast-preview');
  window.broadcastClient.attachPreview(preview);

  document.getElementById('broadcast-btn').addEventListener('click', toggleBroadcast);
};
Enter fullscreen mode Exit fullscreen mode

At this point, we've got our animation source created and added to the client. Next, let's add a createAudioStream() function that will attach the MP3 audio source to the stream.

const createAudioStream = async () => {
  /* Music from Uppbeat (free for Creators!): https://uppbeat.io/t/vens-adams/alone-in-kyoto */
  const audioContext = new AudioContext();
  const mp3 = await fetch('/audio/alone-in-kyoto.mp3');
  const mp3Buffer = await mp3.arrayBuffer();
  const audioBuffer = await audioContext.decodeAudioData(mp3Buffer);
  const streamDestination = audioContext.createMediaStreamDestination();
  const bufferSource = audioContext.createBufferSource();
  bufferSource.buffer = audioBuffer;
  bufferSource.start(0);
  bufferSource.connect(streamDestination);
  bufferSource.loop = true; 
  window.broadcastClient.addAudioInputDevice(streamDestination.stream, 'audio-track');
};
Enter fullscreen mode Exit fullscreen mode

And modify the init() function to add async and call await createAudioStream().

const init = async () => {
  window.broadcastClient = IVSBroadcastClient.create({
    streamConfig: IVSBroadcastClient.BASIC_FULL_HD_LANDSCAPE,
    ingestEndpoint: [Your ingestEndpoint],
  });

  window.video = document.getElementById('src-video');
  window.broadcastClient.addImageSource(window.video, 'video-track', { index: 0 });
  const preview = document.getElementById('broadcast-preview');
  window.broadcastClient.attachPreview(preview);

  await createAudioStream();

  document.getElementById('broadcast-btn').addEventListener('click', toggleBroadcast);
};
Enter fullscreen mode Exit fullscreen mode

Finally, we can define the toggleBroadcast() function to start/stop the stream when the button is clicked.

Note: you'll need to plug in your streamKey at this point. Treat this like any other sensitive credential and protect it from being committed to source control or included in a file that can be accessed by others.

const toggleBroadcast = () => {
  if (!window.isBroadcasting) {
    window.video.play();
    window.broadcastClient
      .startBroadcast([your streamKey])
      .then(() => {
        window.isBroadcasting = true;
      })
      .catch((error) => {
        window.isBroadcasting = false;
        console.error(error);
      });
  }
  else {
    window.broadcastClient.stopBroadcast();
    window.video.pause();
    window.isBroadcasting = false;
  }
};
Enter fullscreen mode Exit fullscreen mode

Now we can save and run the application. Here's how my page looks (with a bit of CSS applied for styling and layout).

Image description

When I click the Broadcast button, the animation begins to play and our live stream is broadcast to our channel.

Note: You will not be able to hear the channel audio from the broadcast tool. We'll look at playback in just a bit.

Image description

Playing Back the Lofi Channel

I've blogged about live stream playback with Amazon IVS before, but we'll create a quick and simple player here to complete the demo. Create an HTML file, include the Amazon IVS player SDK, add a <video> element, create an instance of the player (plug in your playbackUrl), and initialize playback.

<!doctype html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Lofi Radio Playback</title>
  <script src="https://player.live-video.net/1.14.0/amazon-ivs-player.min.js"></script>
  <script>
    document.addEventListener('DOMContentLoaded', () => {
      const ivsPlayer = IVSPlayer.create();
      ivsPlayer.attachHTMLVideoElement(document.getElementById('video-player'));
      ivsPlayer.load([your playbackUrl]);
      ivsPlayer.play();
    })
  </script>
</head>

<body>
  <video id="video-player" controls autoplay playsinline></video>
</body>

</html>
Enter fullscreen mode Exit fullscreen mode

And now we can playback our lofi radio stream in it's own page!


The nice thing about our code is that both the audio and animation will continue on a loop until your channel stops broadcasting.

Bonus: Broadcasting Without a Browser

Another option for broadcasting your own lofi stream is to avoid having any user interface and broadcast in headless mode with FFMPEG (more about that here). Here's an example command that would work for this channel.

$ ffmpeg \
    -re \
    -stream_loop -1 \
    -i ./public/video/lofi.mp4 \
    -stream_loop -1 \
    -i ./public/audio/alone-in-kyoto.mp3 \
    -map 0:v:0 \
    -map 1:a:0 \
    -c:v libx264 \
    -b:v 6000K \
    -maxrate 6000K \
    -pix_fmt yuv420p \
    -s 1920x1080 \
    -profile:v main \
    -preset veryfast \
    -force_key_frames "expr:gte(t,n_forced*2)" \
    -x264opts "nal-hrd=cbr:no-scenecut" \
    -acodec aac \
    -ab 160k \
    -ar 44100 \
    -f flv \
    $DEMO_STREAM_INGEST_ENDPOINT/$DEMO_STREAM_KEY
Enter fullscreen mode Exit fullscreen mode

Summary

In this post, we learned how to create our own lofi radio live stream with Amazon IVS. To make your own lofi stream even more interactive, add live chat with both automated and manual chat moderation. If you have any questions about this demo, please leave a comment below.

Shameless Plug

To learn more about Amazon IVS, tune in to Streaming on Streaming on the AWS Twitch channel every Wednesday at 4pm ET.

Latest comments (12)

Collapse
 
k1lgor profile image
k1lgor

Hi Todd, I am following your guide step by step but at the end nothing happened. Not to mention that you have not added some steps regarding usage of aws cli - need to be configured, to create a user by IAM, create custom policy and add it to the user's permission so can create a channel via cli.

Collapse
 
tythos profile image
Brian Kirkpatrick

If you are having issues getting the IAM policies set up for the IVS actions: docs.aws.amazon.com/ivs/latest/use...

Collapse
 
recursivecodes profile image
Todd Sharp

Thanks for sharing, Brian!

Collapse
 
kensavage profile image
Ken Savage

Very cool post Todd @recursivecodes

Is there a way to build this and also stream it to places like YouTube and Spotify?

Collapse
 
recursivecodes profile image
Todd Sharp

You could probably do something with an app like Streamyard (which supports streaming to multiple destinations). Maybe I'll blog something like this in the future!

Collapse
 
k1lgor profile image
k1lgor

hmmm, this looks interesting but is it possible to stream deep house for example, not only lofi music?

Collapse
 
recursivecodes profile image
Todd Sharp

You can stream any kind of music that you'd like!

Collapse
 
chucklapress profile image
Chuck LaPress

Great article Todd, I can’t wait to build and add the chat and experiment.
Thanks so much for posting.

Collapse
 
recursivecodes profile image
Todd Sharp

Looking forward to see what you cook up! 🔥

Collapse
 
michaeltharrington profile image
Michael Tharrington

Maybe it's time that someone built an entire live streaming UGC platform that gives users the ability to create their own custom lofi channels and broadcast them to their friends? That's a free idea - run with it!

That is a seriously awesome free idea, haha! It'd be cool to have a community where everyone could create their own lofi playlist, customize the character's looks and setting, chat with others... ya got my wheels turning!

Anywho, this was a really cool tutorial that shows how ya might use the Web Broadcast SDK & Amazon IVS in combo to create a lofi radio live stream. Appreciate ya sharing!

Collapse
 
recursivecodes profile image
Todd Sharp

Thanks, Michael! I would love to see someone build something like this!

You can also check out our live stream from the other day where we talked about this and walked through building it live.

twitch.tv/videos/1725282471

Have a great day!

Collapse
 
michaeltharrington profile image
Michael Tharrington

Oooo awesome! Thanks for linking this over to me. 🙌

Hope you're enjoying your weekend!