This article was co-authored by @dayani_yani
Table of Contents
I.Introduction
II.Features of DJ.MODE
III.The Core Architecture: Main vs. Renderer
IV.Setting Up Your Project
V.Crafting the UI (The Renderer)
VI.Designing UI with CSS
VII.Adding Logic (The Renderer)
VIII.Launching Your App
I.Introduction
Building desktop applications used to mean learning and mastering complex, platform specific languages like C++ or Java. However, with Electron JS, you can build cross-platform desktop apps using the web tools you already know - HTML, CSS, and JavaScript. In this tutorial, we will build DJ.MODE, a retro styled music player that allows you to load and play local audio files directly from your desktop.
II.Features of DJ.MODE
- Local Audio Playback: Play MP3 and other audio files seamlessly from your local storage.
- Custom Playlist Support: Load multiple tracks at once and navigate through them.
- Dynamic UI: A pixel art inspired interface with a functional progress bar and playback controls.
III.The Core Architecture: Main vs. Renderer
Before diving into the code, it is important to understand the “brain” and the “face” of an Electron app:
- Main Process: The “brain” that interacts with your operating system.
- Renderer Process: The "face" of your app - the HTML and CSS that the user sees. Electron combines Chromium (the engine behind Chrome) and Node.js to make this possible.
IV.Setting Up Your Project
To use Electron, you first need Node.js (LTS version) installed from nodejs.org. Node.js includes npm, the tool that will download Electron for you.
Windows & macOS
- Visit nodejs.org.
- Download the LTS (Long Term Support) version.
- Run the installer and click "Next" until finished. Once installed, open your terminal and run the following commands to initialize your project:
Bash
mkdir DJ.MODE
cd DJ.MODE
npm init -y
npm install electron --save-dev
Update your package.json file to include the start script:
JSON
"scripts": {
"start": "electron ."
}
Create a main.js file. This script tells Electron how to configure and display your application window.
JavaScript
const { app, BrowserWindow } = require('electron');
function createWindow() {
const win = new BrowserWindow({//this will create a new window for our app
width: 480,
height: 300,
frame: false, //this removes the default window frame
resizable: false,
webPreferences: {//these will allow us to use Node.js features in our script.js
nodeIntegration: true,
contextIsolation: false
}
});
win.setMenu(null); //this removes the default menu bar
win.loadFile('index.html'); //this will load our index.html
}
app.whenReady().then(createWindow);
V.Crafting the UI (The Renderer)
Create a index.html file and link the Press Start 2P font from Google Fonts for a pixelated look.
HTML
<!DOCTYPE html>
<html>
<head>
<title>DJ>MODE</title>
<!--this is a google font link for a pixelated font-->
<link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css"><!--this links to our style.css-->
</head>
<body>
<div id="close-btn-container">
<button id="close-btn">X</button><!--this button will be used to close the application-->
</div>
<div class="main-player-wrapper">
<h1 id="app-title">DJ.MODE</h1>
<h3 id="song-title">NO TRACK LOADED</h3><!--default text-->
<div class="progress-container" id="progress-container">
<div class="progress-bar" id="progress-bar"></div><!--this will be updated by our script.js-->
</div>
<div class="control-group">
<button id="prev-btn" class="ctrl-btn">PREV</button>
<button id="play-pause-btn" class="ctrl-btn main-btn">PLAY</button>
<button id="next-btn" class="ctrl-btn">NEXT</button> <!--these buttons will be handled by our script.js-->
</div>
<button id="upload">LOAD PLAYLIST</button> <!--this button will open a file dialog to load a playlist-->
</div>
<audio id="audio-player"></audio> <!--this will be our audio player-->
<script src="script.js"></script> <!--this links to our script.js that will handle the logic of our player-->
</body>
</html>
VI.Designing UI with CSS
Create a style.css file.To make the app draggable without a standard frame, use -webkit-app-region: drag.
CSS
body {
font-family: 'Press Start 2P', cursive;
background-color: #011638;
color: #EFF0F2;
user-select: none;/*prevents text selection while dragging*/
border-radius: 3px;
overflow: hidden;
border: 3px solid #EEC643;
/* this centers the content vertically and horizontally */
padding: 0;
margin: 0;
text-align: center;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh;
box-sizing: border-box;
-webkit-app-region: drag; /*allows you to drag the window by clicking anywhere*/
}
.main-player-wrapper {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
padding-top: 20px;
}
#close-btn-container { /*this keeps the close button on the top right corner*/
position: absolute;
top: 15px;
right: 20px;
-webkit-app-region: drag;
z-index: 10;
}
#close-btn {
background:none;
color: #EEC643;
border:none;
cursor:pointer;/*this makes the cursor a pointer when hovering over the buttons*/
font-weight: bold;
-webkit-app-region: no-drag;
}
#close-btn:hover {
color: #ffe347;
}
.progress-container {
background: #13284b;
border-radius: 5px;
cursor: pointer;
margin: 10px 0;
height: 6px;
width: 70%;
-webkit-app-region: no-drag;
}
.progress-bar {
background: linear-gradient(to right, #EEC643,#ecd893);
border-radius: 5px;
height: 100%;
width: 0%; /*this will be updated dynamically with our script.js*/
box-shadow: 0 0 10px #0D21A1;
}
.control-group {
display: flex;
align-items: center;
justify-content: center;
gap: 20px;/*this adds space between the buttons*/
margin: 10px 0;
-webkit-app-region: no-drag;
}
.ctrl-btn {
background: #EEC643;
border: 1px solid #0D21A1;
color: #011638;
border-radius: 50%;
width: 40px;
height: 40px;
font-size: 8px;
cursor: pointer;
transition: all 0.2s ease;
display: flex;
justify-content: center;
align-items: center;
-webkit-app-region: no-drag;
}
.main-btn {
width: 55px;
height: 55px;
font-size: 10px;
background: #EEC643;
box-shadow: 0 0 15px #011638;
}
.ctrl-btn:hover {
transform: scale(1.1);/*this makes the buttons grow when hovered*/
box-shadow: 0 0 20px #0D21A1;
background: #ffe347;
}
#upload {
background: transparent;
border: 1px dashed #EEC643;
border-radius: 5px;
padding: 5px 15px;
font-size: 10px;
color: #d6af2e;
cursor: pointer;
-webkit-app-region: no-drag;
margin-top: 5px;
}
#app-title {
font-size: 25px;
letter-spacing: 2px;
color:#EEC643;
margin: 5px 0;
}
#song-title {
font-size: 15px;
margin-bottom: 10px;
max-width: 80%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;/*this will add "..." if the title is too long*/
line-height: 1.5;
}
audio { display: none; }/*this hides the default audio controls
since we are making our own custom controls*/
VII.Adding Logic (The Renderer)
In script.js, we handle the audio logic. A key feature is using URL.createObjectURL(file) to create a temporary link to your local music files so the browser engine can play them.
JavaScript
const { ipcRenderer } = require('electron');
//these references to the HTML elements in our index.html
const audio = document.getElementById('audio-player');
const playBtn = document.getElementById('play-pause-btn');
const songTitle = document.getElementById('song-title');
const progressBar = document.getElementById('progress-bar');
const progressContainer = document.getElementById('progress-container');
const uploadBtn = document.getElementById('upload');
const closeBtn = document.getElementById('close-btn');
let playlist = [];//this will store the list of uploaded songs
let currentTrackIndex = 0;//this is to keep track of the current song playing
uploadBtn.addEventListener('click', () => {//this will trigger the file input dialog when the upload button is clicked
const input = document.createElement('input');
input.type = 'file';
input.accept = 'audio/*';
input.multiple = true;
input.onchange = (e) => {
playlist = Array.from(e.target.files);//this will store the selected files in the playlist array
if (playlist.length > 0) {
currentTrackIndex = 0;
loadAndPlay(currentTrackIndex);//this will load and play the first selected song
}
};
input.click();//this will open the file dialog
});
function loadAndPlay(index) {//this function will load and play the song at the given index in the playlist
if (playlist.length > 0) {
const file = playlist[index];
const url = URL.createObjectURL(file);/*this will create a temporary URL for the selected file
so that we can play it in the audio element*/
audio.src = url;//this will set the audio source to the selected file
songTitle.innerText = file.name.toUpperCase();
audio.play();//this will start playing the song
playBtn.innerText = "PAUSE";//this will show "PAUSE"on the button when the song is playing
}
}
playBtn.addEventListener('click', () => {
if (!audio.src) return; //this will not do anything if no song is loaded
if (audio.paused) {
audio.play();
playBtn.innerText = "PAUSE";//this will show "PAUSE" on the button when the song is playing
} else {
audio.pause();
playBtn.innerText = "PLAY";//while this will show "PLAY" on the button when the song is paused
}
});
document.getElementById('next-btn').addEventListener('click', () => {
if (playlist.length > 0) {
currentTrackIndex = (currentTrackIndex + 1) % playlist.length;//this will move to the next song in the playlist, and loop back to the first song if we are at the end
loadAndPlay(currentTrackIndex);
}
});
document.getElementById('prev-btn').addEventListener('click', () => {
if (playlist.length > 0) {
currentTrackIndex = (currentTrackIndex - 1 + playlist.length) % playlist.length;//this will move to the previous song in the playlist, and loop to the last song if we are at the beginning
loadAndPlay(currentTrackIndex);
}
});
audio.addEventListener('ended', () => {//this will automatically play the next song when the current song ends
if (playlist.length > 0) {
currentTrackIndex = (currentTrackIndex + 1) % playlist.length;
loadAndPlay(currentTrackIndex);
}
});
audio.addEventListener('timeupdate', () => {//this will update the progress bar as the song plays
if (audio.duration) {
const progressPercent = (audio.currentTime / audio.duration) * 100;//this will calculate the percentage of the song that has played
progressBar.style.width = `${progressPercent}%`;//this will set the width of the progress bar based on the percentage of the song that has played
}
});
progressContainer.addEventListener('click', (e) => {//this will allow the user to seek to a different part of the song by clicking on the progress bar
if (!audio.src) return;
const width = progressContainer.clientWidth;//this will get the width of the progress container
const clickX = e.offsetX;//this will get the X coordinate of the click relative to the progress container
const duration = audio.duration;
audio.currentTime = (clickX / width) * duration;//this will set the current time of the audio based on where the user clicked on the progress bar
});
closeBtn.addEventListener('click', () => {
window.close(); //this will close the application when the close button is clicked
});
VII.Launching Your App
To see your creation in action, go back to your terminal and run
Bash
npm start
You should see a window pop up! Click the "Load Playlist" button, pick one or multiple MP3 from your computer, and enjoy your hand-built music player.
Conclusion:
You have just built a functional desktop app using the same tools used to create or build websites. However this is just the beginning of your project, you can continue to expand your desktop app by adding more advanced features and other customized functions to enhance user experience.
Check out these Tutorials:
The BEST way to Create Desktop Apps (Electron)
how i code music player app🎶
GitHub Repository:
https://github.com/a-xine/DJ.MODE/tree/main


Top comments (0)