# Hybrid Exercises
The Hybrid project is to create an HTML5 media player that includes a playlist of mp3 files with CSS animations, JavaScript-powered controls, and is hosted through Github and Netlify with Continuous Delivery.
Table of Contents
# 1. Project Base
In step one of this project you will be designing and building the basic layout of your media player with HTML and CSS. No functionality yet. The buttons for your player will need to have icons on them. We will be using Material Icons for the button faces.
# The Git Repo
You will need to create a private GitHub repo called mad9022-media-hybrid
. Start with just the default main branch. Clone your repo locally before you start to add files.
Remember to do LOTS of git add -A
and git commit -m
commands. You want to create a history of the work that you have done.
# The buttons
Go to the Icons section of Google Fonts (opens new window) and make sure the Audio & Video category is selected.
The icons that you will need are:
- skip_previous
- replay_10
- play_arrow
- pause
- stop
- forward_10
- skip_next
These buttons should all be the same size and appear from left to right in the order shown above. However, keep in mind that we will only ever want to show one of the play and pause icons at a time. This can be done with two buttons and alternating which one is shown, or by changing the icon shown inside a single button. When a song is playing we will add an active
class to the player div. This can be the signal to show only the pause button.
Here is a basic example of how you would add a play icon into a button. The link tag goes in the head and the rest goes in the body where you want your button.
<link
href="https://fonts.googleapis.com/icon?family=Material+Icons"
rel="stylesheet"
/>
<button id="btnPlay" class="buttons">
<span class="material-icons-outlined">play_arrow</span>
</button>
2
3
4
5
6
7
8
Here (opens new window) is the full guide to using Material Icons. You can either download the font and store it in your project or access it from the Google link used in the HTML code sample.
If you want to see a couple approaches to swapping the play and pause button, here is a code GIST with a simple demo (opens new window)
# The layout
The layout for your page should be mobile-first responsive and have two areas - the player area and the playlist area. At mobile sizes the playlist should be below the player. At laptop sizes the player and the playlist should be side-by-side. The font-sizes should adjust to make better use of the available screen space too.
You can use device mode in Chrome to test your layout for mobile.
Use this button to toggle the mobile simulation.
Sample layout:
<header>
<h1>Your Username<h1>
</header>
<main>
<section id="player-area"></section>
<section id="playlist-area"></section>
</main>
2
3
4
5
6
7
# The media player
Our media player needs the row of buttons, described above, but it also needs to have a progress bar, and two time values - one for the total length of the audio track, and one for the current time. For now, use 00:00
as the display value for both <time>
elements.
There also needs to be a place to display the thumbnail and a visualization that appears while the tracks are playing. Here is a sample of what the player HTML COULD be. Your HTML does not have to be the same.
<div id="player" class="is-playing">
<div id="visual">
<img src="./media/thumbnail.jpg" alt="track name" />
<div id="audio-animation"></div>
</div>
<div id="controls">
<!-- all the buttons go here -->
</div>
</div>
2
3
4
5
6
7
8
9
# The playlist
The playlist needs to display the thumbnail image, the artist name, and the track name for each of the mp3
files that you have.
In your CSS there should be a visual affect that happens to a list item when it is clicked. This can be done by adding an active
CSS class name to the list item when the user clicks on an item. The same CSS class name should be applied to the list item when the track is currently playing.
In this first phase of the project we will not have any of the JavaScript controls working so we will just need to add the active
class name to the first item in the list as a visual representation of what will happen.
In this first version, just hard code the track information into your HTML. In step 2 we will build an Array to hold the data and dynamically build the playlist.
# The audio element
You will need to add an HTML <audio id="audio"></audio>
element to your page somewhere and give it an id so that it can be accessed by JavaScript. However, we will hide it with CSS. We are building our own custom media player controls that will look the same across browsers. This one is just so we can load our media files.
MDN reference for the audio element (opens new window)
# The audio files
You will need to create a media
folder in your project and add your own mp3
files. Please add between 5 and 10 files.
Also add to the media
folder a thumbnail image for each mp3
file.
All the thumbnails should be the same size and quality. They should all be square images.
Make sure that ALL the file names are written in lowercase with NO spaces.
The names of the thumbnails should be the same as the names for the mp3
files except that they have a different file extension - .mp3
and .jpg
.
# Submission
To submit this exercise, you will need to invite your instructor to your private repo.
In BS LMS you will need to submit the URL of your repo.
Due Week 2 (See BS LMS for the exact time and date for submission)
# 2. Feature Branch and Controls
In the second phase of this project we will be adding some JavaScript controls and starting to work with Git branches. We need to add the functionality to our audio
element so that our buttons make it play
, pause
, stop
, display the length of the track, and display the current time.
Before starting to work on the JS control of the player, take some time to make any recommended changes to the main
branch and add
, commit
, and push
those changes to the github repo.
Once you are ready to start writing your JavaScript, take a minute to decide how you want to organize your code. Do you want to use namespaces
? Do you want to use es modules
? Think about what you need to code and how you want to organize and name it before you start.
While not required, you are strongly encouraged to use a namespace to contain your APP code.
ES modules are also not required, but strongly encouraged.
Best Practices
When writing your functions, keep them small. Make them do one thing.
Need to do lots of things call lots of small functions in the order that you want them to run.
Never try to do everything at once. Accomplish one thing first then move on to the next.
# Create a controls branch
After your updates to the interface, we are going to create a new branch that will we use for this step and the next one.
git checkout -b controls
This will create the new branch called controls
and switch you over to it. From this point on, when you want to upload to git you will need to do this:
git push origin controls
Note the change in the branch name.
# Audio Events
The audio
element has a lot of events that we can listen for, including:
canplay
canplaythrough
ended
durationchange
pause
play
seeking
seeked
timeupdate
You can tell your script to run a function when one of these events happens, just like you would add a click listener to a button.
let audio = document.getElementById('audio');
audio.addEventListener('ended', playNextTrack);
audio.addEventListener('play', startAnimations);
audio.addEventListener('durationchange', updateTotalTime);
audio.addEventListener('timeupdate', updateCurrentTime);
2
3
4
5
See the full Audio Event reference list (opens new window)
Here is the reference for HTMLMediaElements (opens new window). This is where you will find all the methods and properties we need.
If you want your audio element to actually play or stop a track then we listen for a button click and in the listener function call the play()
or pause()
method.
The difference between
pause
andstop
is thatstop
will take you back to position 0 in the song.
const SONGS = [
//list of song objects
];
const APP = {
player: null,
audio: null,
btnPlay: null,
currentTrack: 0,
init: () => {
(APP.player = document.getElementById('player')),
(APP.audio = document.getElementById('audio')),
(APP.btnPlay = document.getElementById('btnPlay')),
APP.addListeners();
},
addListeners: () => {
APP.btnPlay.addEventListener('click', APP.playTrack);
APP.btnStop.addEventListener('click', APP.stopTrack);
},
playTrack: (ev) => {
if (!APP.audio.paused) return; //already playing
APP.audio.src = SONGS[APP.currentTrack].src;
APP.audio.play();
APP.startAnimations();
},
stopTrack: (ev) => {
APP.audio.pause();
APP.audio.currentTime = 0;
APP.stopAnimations();
},
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Remember to do LOTS of git add -A
and git commit -m
commands. You want to create a history of the work that you have done.
If you want to display the total time for each of your songs inside your playlist items, then here is a repo with a demo of how to extract the duration from all your mp3 files (opens new window). The downside of doing this, is that it requires the client to download every one of your mp3 files immediately, even if they are not going to play them. The real world alternative would be to have this information embedded in the array of song objects, ready to be used on the page.
# Required Features
For this second part you will be working in the controls
branch, and you need to do the following:
- Create a JavaScript Array of Objects that represent all your
mp3
files. Each object needs the following properties:title
,src
,img
, andartist
. - Use the Array of song objects to generate the playlist HTML. This will replace the hard-coded HTML from step one.
- Listen for the
durationchange
event and update the total time display to show a total number of seconds. - Listen for the click events on the
play
,pause
, andstop
buttons; and make them actually play, pause, and stop the first track from the array. - Display the thumbnail image in the player for the currently playing track.
- Only one of the play and pause buttons should be visible at a time. If the track is playing then the play button should be hidden. If the track is paused or stopped then the pause button should be hidden.
- When a track is playing we need to add a CSS
active
class to the current list item as well as a CSS class to our media player area that can be used later for triggering animations. - The user needs to be able to start a song playing. So, there needs to be click listener(s) for the list items in the playlist area. When a user clicks on a list item, then that song will begin playing. If the user clicks on the list item for the currently playing song you can either restart that song, OR ignore the click.
# Submission
Keep your code on the controls
branch. Do not attempt to merge your code back into the main
branch.
Grading will be done on the controls
branch code.
To submit this exercise, you will need to invite your instructor to your private repo. This should already have been done during the first phase.
In BS LMS you will need to submit the URL of your repo.
Due Week 4 (See BS LMS for the exact time and date for submission)
# 3. Feature Branch and Controls Part 2
In phase 3 of the project we need to add the controls for the skip_previous
, skip_next
, replay_10
, and forward_10
buttons. We will still be on the controls
branch for all this work.
The required elements for this step are:
- Add functionality for the
skip_previous
andskip_next
buttons. Clicking either button will stop the current track playing if it is, change thesrc
for the audio element, and start the new tracksrc
playing. - The
replay_10
andforward_10
buttons will change thecurrentTime
value of the audio element by 10 seconds in the appropriate direction. Make sure that the new value does not exceed the total length or go below zero. - Use a globally accessible variable to keep track of the current track number that you are playing. This number will be the index number for the currently playing track from your global array of audio track objects.
You should have a small function for incrementing and a small function for decrementing the current track number. This function needs to take into account the length of the array and zero being the first number. The functions will loop to the other end of the array, when reached. If you are in position 0 and the user clicks skip_previous
then you will be moved to the last item in the array. If you are at the last item in the array and the user clicks skip_next
then you will be moved to position zero.
Here is an example function for moving to next track.
const TRACKS = [
//array of track objects
];
const APP = {
currentTrack: 0, //the current track to play
init: ()=>{ },
nextButtonHandler: (ev)=>{
//user clicked on the next button
//stop audio playing - this could be a function call
//change CSS class indicating that a track is playing - this could be called from stop audio function
//hide pause button and show play button - this could be called from stop audio function
APP.currentTrack = APP.nextTrack();
},
nextTrack: ()=>{
let len = TRACKS.length; //get length of array
APP.currentTrack++; //increment the currentTrack number
if (APP.currentTrack >= len) {
//if the current track number is greater than or equal to the length
APP.currentTrack = 0;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Submission
To submit this exercise, you will need to invite your instructor to your private repo. This should already have been done during the first phase.
Keep your code on the controls
branch. Do not attempt to merge your code back into the main
branch.
Grading will be done on the controls
branch.
In BS LMS you will need to submit the URL of your repo.
Due Week 6 (See BS LMS for the exact time and date for submission)
# 4. Progress Bar
In this fourth phase we need to add a display of the progress through the track that updates itself continually while a track is playing. We also need to update the display of the current time and total time. Each time the progress bar gets updated so should the current time. Both the current and total time need to be displayed in the 00:00
format instead of just an integer value of seconds.
This work is also to be done on the controls
branch.
At the end of phase 4 you will need do a pull request and merge the controls
branch into the main
branch.
Progress bars can be built in several ways:
- with the input range slider element. (Least semantically accurate)
- with nested divs. (very backward compatible)
- with a
<progress>
element. (best choice semantically)
<input type="range" id="progress" max="100" value="0" />
<div class="progress-full">
<div class="progress-current"></div>
</div>
<progress max="100" value="0"></progress>
2
3
4
5
6
7
If you have already built your HTML with one approach but you want to change it, you may.
With the <div>
approach, the .progress-full
part will have a border to outline the full size of the bar and no padding. It usually has a dull background color. Using the same hue as the .progress-current
with reduced saturation is a good idea.
The .progress-current
part will have no margin or border but needs to match the height of its parent element. It should have a background-color that is bright and saturated.
The width of the .progress-current
div gets updated every time the current time is updated. Divide the current time by the track duration to get a percentage. Use that percentage as the width of the div.
If you are using the <progress>
approach, then here is a great article about styling them (opens new window)
To apply proper times for current time and duration we need to listen for the durationchanged
and timeupdate
events on the audio
element. (alternatively you could set up your own setInterval
function that runs once or twice per second and checks the values of the currentTime
and duration
values).
MDN currentTime reference (opens new window)
MDN duration reference (opens new window)
Every time the timeupdate
event fires or your setInterval
function runs you will need to recalculate the values of the progress percentage based on the two numeric seconds values. You also need to update the display for the current time and duration. To do this we need to format the seconds as minutes and seconds like this - 00:00
.
Here is how to do the conversion:
# Submission
To submit this exercise, you will need to invite your instructor to your private repo. This should have been done in step one.
After you push your controls
branch up to your repo, do a pull request and merge your controls
branch into your main
branch along with an approval message.
Grading will be done on the main
branch. If there is no code in the main branch for the progressbar or time elements then you will get no marks for it.
In BS LMS you will need to submit the URL of your repo.
Due Week 9 (See BS LMS for the exact time and date for submission)
# 5. Animation Branch
The fifth step is to create an animation with CSS animations or transitions and transforms which will play when any track is playing. This work should be done on a new branch called animate
.
Since the previous step ended with the merging of controls
into main
, we now need to update our local version of the main
branch.
git checkout main
git pull origin main
2
The above commands will switch you to the main
branch and then pull the latest code from github's main
branch into your local main
branch.
Then we want to create the new animate
branch for complete this stage.
git checkout -b animate
This step relies on the code that you wrote previously to set a css class name on your audio player div element any time that a track is playing. Use the existence of that class name to trigger some type of animation that is a visual cue that audio is playing.
<div id="player" class="is-playing">
<div id="visual">
<img src="./media/thumbnail.jpg" alt="track name" />
<div id="audio-animation"></div>
</div>
<div id="controls">
<!-- all the buttons go here -->
</div>
</div>
2
3
4
5
6
7
8
9
# Submission
To submit this exercise, you will need to invite your instructor to your private repo, which should have been done in the first step.
Push your newly created animate
branch up to Github
git add -A
git commit -m "message about what you did in the animate branch"
git push origin animate
2
3
In BS LMS you will need to submit the URL of your repo.
Due Week 11 (See BS LMS for the exact time and date for submission)
# 6. Netlify Hosting
This is the final step to the audio player. We are going to be deploying our audio player app for production now. So, before doing that, go back to apply corrections, error fixes, and enhancements. Refactor and improve your code before exposing it to the world.
The work for this final step should start by having your animate
branch code merged into your main
branch. Do a pull request and a merge of the animate
branch into main
on Github.
At this point the main
and animate
branches will have the same code. The controls
branch will be several commits behind main
.
You can add the mute feature directly in the main
branch (not what we would do in the real world) or in a new mute
branch.
# mute feature
The final feature to add is the ability to mute the player. Go back to the Material Icons font page from step one and find the icons you need to show whether the audio is muted or not. Add the ability for a user to mute and unmute the audio as it plays. The user should be able to mute the audio whether or not a track is currently playing. The audio
element has a muted
property that you can use.
MDN muted reference (opens new window)
After you have the mute feature working, merge || push your updates into the main
branch.
# deployment
Finally, we need to deploy our fantastic audio player.
Refer back to the notes and videos about connecting your Github Repo to Netlify from Week 3.
Set up your new Netlify site so that it updates itself every time you push something new to the main
branch of your private repo.
Netlify Build Notes
It is worth noting that the videos about Netlify included a build
script and a dist
folder. This is NOT a required step.
You do NOT need to create a build
script.
You do NOT need to create a dist
folder.
Netlify is happy to just deploy your whole repo with no build script and no special folder. You can just point it at a Github branch for a repo.
# Submission
To submit this exercise, you will need to invite your instructor to your private repo. This should have been done in the first step.
In BS LMS you will need to submit the URL of your repo AND the URL for your Netlify website.
Due Week 13 (See BS LMS for the exact time and date for submission)