Music is magic. It has the power to create new memories, friends, and unforgettable sensations.
All of us love music but for some, it’s a passion. Listening and sharing different tunes that tap into our emotions is a hobby that strengthens social bonds as well as pulls many friendships together.
But given that we live in a more globalized age where many of our friends are dotted across the globe, trying to organize anything social with them can be difficult.
Being both a music-lover and an innovator, Franco Chen took on this challenge by creating an application that allows users to listen, share and discover new music with their friends, irrespective of their location.
To maximize the user’s experience, the application needed to operate with a database that’s capable of transmitting, processing, and retrieving data in real time. With Redis, Franco was able to create a low latency application where commands and interactions between users were hyper-responsive.
Let’s take a look at how Franco brought this application to life. But before we go any further, we’d like to point out that we also have a range of exciting applications for you to check out on the Redis Launchpad.
You’ll build a platform that allows you to listen to and share music with your friends online. The application works by creating private or public online rooms where you can invite people with different tastes to join in the experience.
In its purest sense, the application is a gateway to venturing deeper into different genres, tastes, and preferences through interacting with people who have a shared appreciation of music. Below we’ll show you how to create this application from scratch by highlighting the different components as well as walking you through each stage of the implementation process.
Ready to get started?
Ok, let’s get started!
Socket.IO: Used as a Javascript library for real-time web apps.
Javascript: Used as the preferred programming language that allows you to make web pages interactive.
RedisJSON: A JSON data type for Redis that allows storing, updating and fetching JSON values from Redis keys
RediSearch: Used for querying, secondary indexing, and full text search for Redis.
Chakra UI: Provides you with basic building blocks that can help you build the front end of your application.
Redux: Used as an open source Javascript library for managing and centralizing application state.
Axios: Used as a promise-based HTTP client for Node.js and the browser.
Node.js: Used as an open source, cross-platform that executes JavaScript code outside of a web browser.
Express: Used as a flexible Node.js web application framework that provides a robust set of features for web and mobile applications.
Prerequisites
git clone https://github.com/spatialdj/frontend
Use the RedisMod docker image to set up the Redis modules. In the root directory of the frontend, type in the following code to install the frontend dependencies:
npm install
Go to the backend’s root directory and create a file called config.js with the following contents:
export default {
redisHost: 'localhost',
redisPassword: 'your_password_for_redis_here',
sessionSecret: 'somesessionsecret',
passwordSaltRounds: 10,
youtube_key: 'youtube_api_key'
}
Type in the following command to install backend dependencies:
npm install
Run the following command in the backend’s root directory:
npm start
Your app should be running at localhost:3000
Room
messages: Redis key to message this room
id: ID of this room
json: JSON representation of this room (used because RediSearch doesn’t support JSON yet)
numMembers: The number of members currently in the room
description: The description of the room
name: The name of the room
private: (True | False) whether the room is searchable
genres: Genres the room is geared towards
Indices (RediSearch):
name: TEXT
description: TEXT
genres: TAG
numMembers: NUMERIC
private: TAG
private: TAG
Commands:
Get JSON representation of this room
HGET ${roomId} json
Create a new room or update room (not all fields required)
HSET room:${roomId} id ${roomId} name ${name} description ${description} private ${private} genres ${genres} numMembers ${numMembers} json ${roomJson}
Check if a room exists (used when attempting to join room):
EXISTS ${roomId}
Used for rooms page for searching:
FT.SEARCH @name|description:(${searchQuery}) @private:{false} @genres:{${genres}} SORTBY numMembers DESC LIMIT ${offset} ${limit}:
User queue
Session
User
Example playlist:
"db168000-fa58-491b-81d0-1287d866fcf7": {
"id": "db168000-fa58-491b-81d0-1287d866fcf7",
"name": "Text playlist",
"user": "exampleuser",
"queue": [
{
"videoId": "TT4PHY0_hwE",
"title": "Ekcle - Pearl Jigsaw",
"thumbnails": {
"default": {
"url": "https://i.ytimg.com/vi/TT4PHY0_hwE/default.jpg",
"width": 120,
"height": 90
}
},
"channelTitle": "Ekcle",
"id": "2f7473db-2aec-4ec1-a2bd-623ed6b3ce48",
"duration": 348000
}
]
}
Example user:
{
"username": ...,
"password": ...,
"profilePicture": ...,
"playlist": { ... },
"selectedPlaylist": …
}
JSON.SET user:${username} ${path} ${userJson}
JSON.GET user:${username} ${path}
Messages
LPUSH message:${messagesId} ${data}
LRANGE message:${messagesId} ${start} ${end}
Socket
SET socket:${socketId} ${username}
GET socket:${socketId}
DEL socket:${socketId}
JSON.SET user:${username} . ${userJson}
SET sess:${sessionId} ${data}
JSON.GET user:${username} ${path}
SET socket:${socketId} ${username}
DEL socket:${socketId}
FT.SEARCH @name|description:(${searchQuery}) @private:{false} @genres:{${genres}} SORTBY numMembers DESC LIMIT ${offset} ${limit}
HSET room:${roomId} id ${roomId} name ${name} description ${description} private ${private} genres ${genres} numMembers ${numMembers} json ${roomJson}
HSET room:${roomId} id ${roomId} name ${name} description ${description} private ${private} genres ${genres} numMembers ${numMembers} json ${roomJson}
RPUSH queue:${roomId} ${username}
LMOVE queue:${roomId} queue:${roomId} LEFT RIGHT
song = JSON.ARRPOP user:${username} .playlist.${playlistId}.queue 0
JSON.ARRAPPEND user:${username} .playlist.${playlistId}.queue song
JSON.SET user:${username} .playlist.${playlistId} ${playlistJson}
JSON.DEL user:${username} .playlist.${playlistId}
JSON.SET user:${username} .selectedPlaylist ${playlistId})
JSON.ARRAPPEND user:${username} .playlist.${playlistId}.queue ${song}
JSON.GET user:${username} .playlist.${playlistId}.queue
const songs = JSON.parse(await jsonGetAsync(getUserKey(username), `.playlist.${playlistId}.queue`))
const songIndex = songs.findIndex(song => song.id === songId)
const success = songIndex !== -1
if (success) {
songs.splice(songIndex, 1)
}
try {
await jsonSetAsync(getUserKey(username), `.playlist.${playlistId}.queue`, JSON.stringify(songs))
} catch (error) {
return res.status(400).json(error)
}
On the homepage, you can log in or create a new account by clicking on either one of these icons on the navigation bar on the right-hand corner of the screen.
Rooms are online communities that host a range of music depending on the criteria outlined by the host. Here you’ll be able to connect and interact with different people to discover new music based on your preferred tastes and genres.
Once you click on the ‘Create Room’ icon on the homepage, you’ll have to provide a description about the room you want to host. Here you can add a bit of character in the description to communicate the type of vibe and people you want to join in the room, along with the type of music that should be shared here (see below).
Friends who join the room will appear with their character icons (see below).
Once everyone has joined, you’ll need to create a playlist where you can share music. Click on the arrow at the bottom left-hand side of the screen to begin this process. Next, a search bar that’s connected to YouTube will appear.
From here, you can search for the different songs you want to include and add them onto the playlist by clicking on the plus sign. RediSearch will identify and add songs to your playlist based on the phrases entered into the search engine.
When each song is playing, the music video will appear in the center of the screen. You’ll also see a chat window on the right-hand side of the screen where everyone in the room can share their opinion on each song.
At the bottom left-hand corner of the screen, you’ll be able to like or dislike a song. If over half of the people in the room dislike the song, then the application will skip to the next tune on the playlist.
Transmitting reams of data between different components with hyper-efficiency was always going to be one of the biggest obstacles for Franco to overcome. In today’s digi-sphere, users expect commands and responses to be in real time and they expect nothing less.
This was especially important for this application, given the amount of data moving between the server and the client. Leveraging Redis’ advanced data capabilities gave the application low-latency for data retrievals, creating a completely fluid and responsive app that allowed users to listen, share, and comment on each other’s music in real time.
To get a more visual insight into the ins and outs of how this app was created, make sure to give Franco’s YouTube video a watch.
If you’ve enjoyed this post, we have many more for you to dive into on the Redis Launchpad. Here you’ll have access to an exciting range of applications that are having an impact on everyday life around the world.
These include real time vehicle tracking systems, an app that accelerate the blood donation process, split testing software, and much more.
Check them out. Be inspired. And join in the Redis fun.
Franco Chen
Franco has over nine years worth of experience in software engineering and is currently studying at the University of Waterloo. If you want to keep up to date with all of his projects then make sure to follow him on GitHub here.