Introduction to HTML5 Media

HTML5 introduced native support for audio and video content through the <audio> and <video> elements, eliminating the need for plugins like Flash. These elements provide built-in controls and can be customized with JavaScript for enhanced user experiences.

The <audio> Element

The <audio> element embeds sound content in web pages with support for multiple audio formats.

Basic Audio Implementation



html

<!-- Simple audio with controls -->
<audio controls>
  <source src="audio/music.mp3" type="audio/mpeg">
  <source src="audio/music.ogg" type="audio/ogg">
  Your browser does not support the audio element.
</audio>

<!-- Audio with additional attributes -->
<audio controls autoplay loop muted preload="auto">
  <source src="audio/background-music.mp3" type="audio/mpeg">
  <source src="audio/background-music.wav" type="audio/wav">
  <p>Your browser doesn't support HTML5 audio. 
     <a href="audio/background-music.mp3">Download the audio file</a>.</p>
</audio>

Audio Attributes



html

<audio 
  controls          <!-- Show browser's default controls -->
  autoplay         <!-- Start playing automatically (use carefully) -->
  loop             <!-- Repeat playback -->
  muted            <!-- Start muted -->
  preload="auto"   <!-- auto, metadata, or none -->
  volume="0.5"     <!-- Set initial volume (0.0 to 1.0) -->
  src="audio.mp3"> <!-- Direct source (alternative to <source>) -->
</audio>

The <video> Element

The <video> element embeds video content with similar functionality to audio.

Basic Video Implementation



html

<!-- Simple video with controls -->
<video controls width="640" height="360">
  <source src="video/movie.mp4" type="video/mp4">
  <source src="video/movie.webm" type="video/webm">
  <source src="video/movie.ogg" type="video/ogg">
  Your browser does not support the video tag.
</video>

<!-- Video with poster and additional attributes -->
<video 
  controls 
  width="800" 
  height="450" 
  poster="images/video-thumbnail.jpg"
  preload="metadata">
  <source src="video/tutorial.mp4" type="video/mp4">
  <source src="video/tutorial.webm" type="video/webm">
  <track kind="subtitles" src="subtitles/english.vtt" srclang="en" label="English" default>
  <track kind="subtitles" src="subtitles/spanish.vtt" srclang="es" label="Español">
  <p>Your browser doesn't support HTML5 video. 
     <a href="video/tutorial.mp4">Download the video</a>.</p>
</video>

Video Attributes



html

<video 
  controls          <!-- Show playback controls -->
  autoplay         <!-- Auto-start (requires muted in most browsers) -->
  loop             <!-- Repeat playback -->
  muted            <!-- Start muted -->
  preload="auto"   <!-- Loading strategy -->
  poster="thumb.jpg" <!-- Thumbnail image -->
  width="640"      <!-- Display width -->
  height="360"     <!-- Display height -->
  playsinline      <!-- Play inline on mobile (iOS) -->
  crossorigin="anonymous"> <!-- CORS settings -->
</video>

Multiple Source Formats

Different browsers support different media formats. Use multiple sources for maximum compatibility:



html

<!-- Audio format compatibility -->
<audio controls>
  <source src="audio/song.mp3" type="audio/mpeg">     <!-- Chrome, Safari, IE -->
  <source src="audio/song.ogg" type="audio/ogg">      <!-- Firefox, Chrome -->
  <source src="audio/song.wav" type="audio/wav">      <!-- Fallback -->
</audio>

<!-- Video format compatibility -->
<video controls width="640" height="360">
  <source src="video/movie.mp4" type="video/mp4">     <!-- Most browsers -->
  <source src="video/movie.webm" type="video/webm">   <!-- Chrome, Firefox -->
  <source src="video/movie.ogv" type="video/ogg">     <!-- Firefox, older versions -->
</video>

Subtitles and Captions with <track>

The <track> element provides text tracks for accessibility:



html

<video controls width="640" height="360">
  <source src="video/lecture.mp4" type="video/mp4">
  
  <!-- Subtitles in multiple languages -->
  <track kind="subtitles" src="tracks/en.vtt" srclang="en" label="English" default>
  <track kind="subtitles" src="tracks/es.vtt" srclang="es" label="Español">
  <track kind="subtitles" src="tracks/fr.vtt" srclang="fr" label="Français">
  
  <!-- Captions for accessibility -->
  <track kind="captions" src="tracks/captions-en.vtt" srclang="en" label="English Captions">
  
  <!-- Chapter navigation -->
  <track kind="chapters" src="tracks/chapters.vtt" srclang="en" label="Chapters">
  
  <!-- Additional descriptions -->
  <track kind="descriptions" src="tracks/descriptions.vtt" srclang="en" label="Audio Descriptions">
</video>

WebVTT Format Example



vtt

WEBVTT

00:00:00.000 --> 00:00:03.000
Welcome to our HTML tutorial series.

00:00:03.000 --> 00:00:06.000
Today we'll learn about media elements.

00:00:06.000 --> 00:00:10.000
Let's start with the audio and video tags.

Custom Media Controls with JavaScript

Create custom controls for better design integration:



html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Custom Video Player</title>
  <style>
    .video-container {
      position: relative;
      max-width: 800px;
      margin: 20px auto;
      background: #000;
      border-radius: 8px;
      overflow: hidden;
    }
    
    video {
      width: 100%;
      height: auto;
      display: block;
    }
    
    .custom-controls {
      position: absolute;
      bottom: 0;
      left: 0;
      right: 0;
      background: linear-gradient(transparent, rgba(0,0,0,0.7));
      padding: 20px;
      opacity: 0;
      transition: opacity 0.3s;
    }
    
    .video-container:hover .custom-controls {
      opacity: 1;
    }
    
    .controls-row {
      display: flex;
      align-items: center;
      gap: 15px;
      color: white;
    }
    
    .play-btn {
      background: none;
      border: none;
      color: white;
      font-size: 24px;
      cursor: pointer;
      padding: 5px;
    }
    
    .progress-container {
      flex: 1;
      height: 6px;
      background: rgba(255,255,255,0.3);
      border-radius: 3px;
      cursor: pointer;
      position: relative;
    }
    
    .progress-bar {
      height: 100%;
      background: #007bff;
      border-radius: 3px;
      width: 0%;
      transition: width 0.1s;
    }
    
    .time-display {
      font-size: 14px;
      min-width: 80px;
    }
    
    .volume-container {
      display: flex;
      align-items: center;
      gap: 5px;
    }
    
    .volume-slider {
      width: 60px;
    }
    
    .fullscreen-btn {
      background: none;
      border: none;
      color: white;
      font-size: 18px;
      cursor: pointer;
      padding: 5px;
    }
  </style>
</head>
<body>
  <div class="video-container">
    <video id="custom-video" preload="metadata">
      <source src="video/sample.mp4" type="video/mp4">
      <source src="video/sample.webm" type="video/webm">
    </video>
    
    <div class="custom-controls">
      <div class="controls-row">
        <button class="play-btn" id="play-pause">▶</button>
        
        <div class="progress-container" id="progress-container">
          <div class="progress-bar" id="progress-bar"></div>
        </div>
        
        <div class="time-display" id="time-display">0:00 / 0:00</div>
        
        <div class="volume-container">
          <button id="mute-btn">🔊</button>
          <input type="range" class="volume-slider" id="volume-slider" min="0" max="100" value="100">
        </div>
        
        <button class="fullscreen-btn" id="fullscreen-btn">⛶</button>
      </div>
    </div>
  </div>
  
  <script>
    const video = document.getElementById('custom-video');
    const playPauseBtn = document.getElementById('play-pause');
    const progressContainer = document.getElementById('progress-container');
    const progressBar = document.getElementById('progress-bar');
    const timeDisplay = document.getElementById('time-display');
    const muteBtn = document.getElementById('mute-btn');
    const volumeSlider = document.getElementById('volume-slider');
    const fullscreenBtn = document.getElementById('fullscreen-btn');
    
    // Play/Pause functionality
    playPauseBtn.addEventListener('click', () => {
      if (video.paused || video.ended) {
        video.play();
        playPauseBtn.textContent = '⏸';
      } else {
        video.pause();
        playPauseBtn.textContent = '▶';
      }
    });
    
    // Update progress bar
    video.addEventListener('timeupdate', () => {
      const progress = (video.currentTime / video.duration) * 100;
      progressBar.style.width = progress + '%';
      
      // Update time display
      const current = formatTime(video.currentTime);
      const duration = formatTime(video.duration);
      timeDisplay.textContent = `${current} / ${duration}`;
    });
    
    // Seek functionality
    progressContainer.addEventListener('click', (e) => {
      const rect = progressContainer.getBoundingClientRect();
      const clickX = e.clientX - rect.left;
      const width = rect.right - rect.left;
      const newTime = (clickX / width) * video.duration;
      video.currentTime = newTime;
    });
    
    // Volume control
    volumeSlider.addEventListener('input', (e) => {
      video.volume = e.target.value / 100;
      updateVolumeIcon();
    });
    
    muteBtn.addEventListener('click', () => {
      video.muted = !video.muted;
      updateVolumeIcon();
    });
    
    function updateVolumeIcon() {
      if (video.muted || video.volume === 0) {
        muteBtn.textContent = '🔇';
      } else if (video.volume < 0.5) {
        muteBtn.textContent = '🔉';
      } else {
        muteBtn.textContent = '🔊';
      }
    }
    
    // Fullscreen functionality
    fullscreenBtn.addEventListener('click', () => {
      if (document.fullscreenElement) {
        document.exitFullscreen();
      } else {
        document.querySelector('.video-container').requestFullscreen();
      }
    });
    
    // Format time helper
    function formatTime(seconds) {
      if (isNaN(seconds)) return '0:00';
      
      const minutes = Math.floor(seconds / 60);
      const secs = Math.floor(seconds % 60);
      return `${minutes}:${secs.toString().padStart(2, '0')}`;
    }
    
    // Keyboard shortcuts
    document.addEventListener('keydown', (e) => {
      if (e.target.tagName.toLowerCase() !== 'input') {
        switch(e.code) {
          case 'Space':
            e.preventDefault();
            playPauseBtn.click();
            break;
          case 'KeyM':
            muteBtn.click();
            break;
          case 'KeyF':
            fullscreenBtn.click();
            break;
          case 'ArrowLeft':
            video.currentTime -= 10;
            break;
          case 'ArrowRight':
            video.currentTime += 10;
            break;
        }
      }
    });
  </script>
</body>
</html>

Audio Player with Playlist



html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Custom Audio Player with Playlist</title>
  <style>
    .audio-player {
      max-width: 400px;
      margin: 20px auto;
      background: #f8f9fa;
      border-radius: 12px;
      padding: 20px;
      box-shadow: 0 4px 6px rgba(0,0,0,0.1);
    }
    
    .now-playing {
      text-align: center;
      margin-bottom: 20px;
    }
    
    .track-info h3 {
      margin: 0 0 5px 0;
      color: #333;
    }
    
    .track-info p {
      margin: 0;
      color: #666;
      font-size: 14px;
    }
    
    .audio-controls {
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 15px;
      margin: 20px 0;
    }
    
    .control-btn {
      background: #007bff;
      border: none;
      border-radius: 50%;
      width: 50px;
      height: 50px;
      color: white;
      font-size: 18px;
      cursor: pointer;
      transition: background-color 0.3s;
    }
    
    .control-btn:hover {
      background: #0056b3;
    }
    
    .play-pause-btn {
      width: 60px;
      height: 60px;
      font-size: 24px;
    }
    
    .progress-section {
      margin: 15px 0;
    }
    
    .progress-bar {
      width: 100%;
      height: 6px;
      background: #e9ecef;
      border-radius: 3px;
      cursor: pointer;
      position: relative;
    }
    
    .progress-fill {
      height: 100%;
      background: #007bff;
      border-radius: 3px;
      width: 0%;
      transition: width 0.1s;
    }
    
    .time-info {
      display: flex;
      justify-content: space-between;
      font-size: 12px;
      color: #666;
      margin-top: 5px;
    }
    
    .playlist {
      max-height: 200px;
      overflow-y: auto;
      margin-top: 20px;
    }
    
    .playlist-item {
      display: flex;
      align-items: center;
      padding: 10px;
      border-radius: 6px;
      cursor: pointer;
      transition: background-color 0.3s;
    }
    
    .playlist-item:hover {
      background: #e9ecef;
    }
    
    .playlist-item.active {
      background: #007bff;
      color: white;
    }
    
    .playlist-item .track-number {
      width: 30px;
      text-align: center;
      font-weight: bold;
    }
    
    .playlist-item .track-details {
      flex: 1;
      margin-left: 10px;
    }
    
    .playlist-item .track-title {
      font-weight: bold;
      margin-bottom: 2px;
    }
    
    .playlist-item .track-artist {
      font-size: 12px;
      opacity: 0.8;
    }
  </style>
</head>
<body>
  <div class="audio-player">
    <audio id="audio-element" preload="metadata"></audio>
    
    <div class="now-playing">
      <div class="track-info">
        <h3 id="current-title">Select a track</h3>
        <p id="current-artist">No track selected</p>
      </div>
    </div>
    
    <div class="audio-controls">
      <button class="control-btn" id="prev-btn">⏮</button>
      <button class="control-btn play-pause-btn" id="play-pause-btn">▶</button>
      <button class="control-btn" id="next-btn">⏭</button>
    </div>
    
    <div class="progress-section">
      <div class="progress-bar" id="progress-bar">
        <div class="progress-fill" id="progress-fill"></div>
      </div>
      <div class="time-info">
        <span id="current-time">0:00</span>
        <span id="total-time">0:00</span>
      </div>
    </div>
    
    <div class="playlist" id="playlist">
      <!-- Playlist items will be generated by JavaScript -->
    </div>
  </div>
  
  <script>
    const audio = document.getElementById('audio-element');
    const playPauseBtn = document.getElementById('play-pause-btn');
    const prevBtn = document.getElementById('prev-btn');
    const nextBtn = document.getElementById('next-btn');
    const progressBar = document.getElementById('progress-bar');
    const progressFill = document.getElementById('progress-fill');
    const currentTimeEl = document.getElementById('current-time');
    const totalTimeEl = document.getElementById('total-time');
    const currentTitle = document.getElementById('current-title');
    const currentArtist = document.getElementById('current-artist');
    const playlistEl = document.getElementById('playlist');
    
    // Sample playlist data
    const playlist = [
      {
        title: "Sample Track 1",
        artist: "Demo Artist",
        src: "audio/track1.mp3"
      },
      {
        title: "Sample Track 2", 
        artist: "Demo Artist",
        src: "audio/track2.mp3"
      },
      {
        title: "Sample Track 3",
        artist: "Demo Artist", 
        src: "audio/track3.mp3"
      }
    ];
    
    let currentTrackIndex = 0;
    let isPlaying = false;
    
    // Initialize playlist
    function initPlaylist() {
      playlistEl.innerHTML = '';
      playlist.forEach((track, index) => {
        const item = document.createElement('div');
        item.className = 'playlist-item';
        item.innerHTML = `
          <div class="track-number">${index + 1}</div>
          <div class="track-details">
            <div class="track-title">${track.title}</div>
            <div class="track-artist">${track.artist}</div>
          </div>
        `;
        item.addEventListener('click', () => playTrack(index));
        playlistEl.appendChild(item);
      });
    }
    
    // Play specific track
    function playTrack(index) {
      currentTrackIndex = index;
      const track = playlist[index];
      
      audio.src = track.src;
      currentTitle.textContent = track.title;
      currentArtist.textContent = track.artist;
      
      // Update active playlist item
      document.querySelectorAll('.playlist-item').forEach((item, i) => {
        item.classList.toggle('active', i === index);
      });
      
      audio.load();
    }
    
    // Play/pause functionality
    playPauseBtn.addEventListener('click', () => {
      if (isPlaying) {
        audio.pause();
      } else {
        if (!audio.src) {
          playTrack(0);
        }
        audio.play();
      }
    });
    
    // Audio event listeners
    audio.addEventListener('play', () => {
      isPlaying = true;
      playPauseBtn.textContent = '⏸';
    });
    
    audio.addEventListener('pause', () => {
      isPlaying = false;
      playPauseBtn.textContent = '▶';
    });
    
    audio.addEventListener('timeupdate', () => {
      const progress = (audio.currentTime / audio.duration) * 100;
      progressFill.style.width = progress + '%';
      
      currentTimeEl.textContent = formatTime(audio.currentTime);
      totalTimeEl.textContent = formatTime(audio.duration);
    });
    
    audio.addEventListener('ended', () => {
      // Auto-play next track
      nextTrack();
    });
    
    // Previous/Next track
    prevBtn.addEventListener('click', () => {
      currentTrackIndex = (currentTrackIndex - 1 + playlist.length) % playlist.length;
      playTrack(currentTrackIndex);
      if (isPlaying) audio.play();
    });
    
    nextBtn.addEventListener('click', nextTrack);
    
    function nextTrack() {
      currentTrackIndex = (currentTrackIndex + 1) % playlist.length;
      playTrack(currentTrackIndex);
      if (isPlaying) audio.play();
    }
    
    // Progress bar seeking
    progressBar.addEventListener('click', (e) => {
      const rect = progressBar.getBoundingClientRect();
      const clickX = e.clientX - rect.left;
      const width = rect.right - rect.left;
      const newTime = (clickX / width) * audio.duration;
      audio.currentTime = newTime;
    });
    
    // Format time helper
    function formatTime(seconds) {
      if (isNaN(seconds)) return '0:00';
      const minutes = Math.floor(seconds / 60);
      const secs = Math.floor(seconds % 60);
      return `${minutes}:${secs.toString().padStart(2, '0')}`;
    }
    
    // Initialize
    initPlaylist();
  </script>
</body>
</html>

Media Accessibility Best Practices

  1. Always provide fallback content for browsers that don't support HTML5 media
  2. Include captions and subtitles for video content
  3. Provide audio descriptions for visually impaired users
  4. Use appropriate ARIA labels for custom controls
  5. Ensure keyboard navigation works for all controls
  6. Provide transcripts for audio content

Browser Compatibility and Format Support

FormatChromeFirefoxSafariEdgeMP3✅✅✅✅OGG✅✅❌❌WAV✅✅✅✅MP4✅✅✅✅WebM✅✅❌✅