← Back to DevBytes

HTML5 APIs: Complete Guide

Introduction to HTML5 APIs

HTML5 APIs represent a collection of powerful browser-based interfaces that extend the capabilities of modern web applications far beyond simple document rendering. These APIs provide standardized methods for handling geolocation, persistent storage, background processing, real-time graphics, and much more—all without requiring third-party plugins or external frameworks. They form the backbone of modern Progressive Web Apps and rich interactive experiences.

What Are HTML5 APIs?

HTML5 APIs are JavaScript interfaces built into modern web browsers that expose device capabilities, system services, and advanced functionality to web applications. They are defined as part of the HTML5 specification and related W3C/WHATWG standards, providing consistent, cross-browser access to features that were previously only available to native applications. These APIs work alongside HTML markup and CSS styling, accessed entirely through JavaScript running in the browser sandbox.

Why HTML5 APIs Matter

The significance of HTML5 APIs cannot be overstated in today's web ecosystem. They enable web applications to function offline, process data in background threads, create hardware-accelerated 2D/3D graphics, access device sensors, manage browser history programmatically, and deliver native-like experiences. This convergence of web and native capabilities has transformed what users expect from a web application. Developers can now build single-page applications, offline-first experiences, real-time collaboration tools, and immersive visualizations—all deployable instantly via a URL with no installation required.

Geolocation API

🚀 Deploy your AI agent in 10 minutes

Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.

Try it free →

Overview and Use Cases

The Geolocation API allows web applications to access the user's physical location, subject to user permission. Common use cases include store finders, delivery tracking, localized content, fitness tracking apps, and real-time navigation assistants. The API supports both one-time position queries and continuous location monitoring through watch methods.

Basic Implementation

The Geolocation API is accessed through navigator.geolocation. Before calling any method, you should verify browser support and handle permission scenarios gracefully. Here is a complete working example that obtains the user's position and displays it:

// Check for Geolocation API support
if ('geolocation' in navigator) {
  const geoOptions = {
    enableHighAccuracy: true,
    timeout: 10000,
    maximumAge: 300000
  };

  function successCallback(position) {
    const latitude = position.coords.latitude;
    const longitude = position.coords.longitude;
    const accuracy = position.coords.accuracy;
    const altitude = position.coords.altitude;
    const speed = position.coords.speed;
    const timestamp = new Date(position.timestamp);

    console.log(`Latitude: ${latitude.toFixed(6)}`);
    console.log(`Longitude: ${longitude.toFixed(6)}`);
    console.log(`Accuracy: ${accuracy} meters`);
    console.log(`Timestamp: ${timestamp.toLocaleString()}`);

    // Display on a map or update UI
    document.getElementById('location-display').innerHTML = `
      You are at ${latitude.toFixed(4)}, ${longitude.toFixed(4)}
      (accurate to within ${Math.round(accuracy)} meters)
    `;
  }

  function errorCallback(error) {
    let message = '';
    switch(error.code) {
      case error.PERMISSION_DENIED:
        message = 'User denied the geolocation request.';
        break;
      case error.POSITION_UNAVAILABLE:
        message = 'Location information is unavailable.';
        break;
      case error.TIMEOUT:
        message = 'The request to get user location timed out.';
        break;
      default:
        message = 'An unknown error occurred.';
        break;
    }
    console.error('Geolocation error:', message);
    document.getElementById('location-display').textContent = message;
  }

  navigator.geolocation.getCurrentPosition(successCallback, errorCallback, geoOptions);
} else {
  console.error('Geolocation is not supported by this browser.');
}

Watching Position Changes

For real-time tracking scenarios, use watchPosition() which returns a watch ID that can be cleared later:

let watchID = null;

function startWatching() {
  watchID = navigator.geolocation.watchPosition(
    (position) => {
      console.log(`Updated position: ${position.coords.latitude}, ${position.coords.longitude}`);
      // Update a real-time map marker here
    },
    (error) => {
      console.error('Watch error:', error.message);
    },
    { enableHighAccuracy: true, maximumAge: 0, timeout: 5000 }
  );
}

function stopWatching() {
  if (watchID !== null) {
    navigator.geolocation.clearWatch(watchID);
    watchID = null;
    console.log('Stopped watching position.');
  }
}

Best Practices for Geolocation

Web Storage API

Understanding localStorage and sessionStorage

The Web Storage API provides two mechanisms for storing key-value pairs on the client: localStorage persists data indefinitely across browser sessions, while sessionStorage clears data when the tab or window closes. Both offer a synchronous, string-based interface with approximately 5–10 MB of storage per origin, depending on the browser.

Complete localStorage Example

// Check if storage is available
function storageAvailable(type) {
  try {
    const storage = window[type];
    const testKey = '__storage_test__';
    storage.setItem(testKey, testKey);
    storage.removeItem(testKey);
    return true;
  } catch (e) {
    return e instanceof DOMException &&
      (e.code === 22 || e.code === 1014 ||
       e.name === 'QuotaExceededError' ||
       e.name === 'NS_ERROR_DOM_QUOTA_REACHED') &&
      storage && storage.length !== 0;
  }
}

if (storageAvailable('localStorage')) {
  // Storing complex data requires serialization
  const userPreferences = {
    theme: 'dark',
    fontSize: 16,
    notifications: true,
    lastVisit: new Date().toISOString()
  };

  // Save to localStorage
  localStorage.setItem('preferences', JSON.stringify(userPreferences));
  localStorage.setItem('visitCount', '42');

  // Retrieve and parse
  const savedPreferences = JSON.parse(localStorage.getItem('preferences'));
  const visitCount = parseInt(localStorage.getItem('visitCount'), 10) || 0;

  console.log('Loaded preferences:', savedPreferences);
  console.log('Visit count:', visitCount);

  // Increment visit count
  localStorage.setItem('visitCount', (visitCount + 1).toString());

  // Remove a specific item
  localStorage.removeItem('temporaryData');

  // Clear all storage for this origin
  // localStorage.clear();

  // Iterate through all stored keys
  for (let i = 0; i < localStorage.length; i++) {
    const key = localStorage.key(i);
    const value = localStorage.getItem(key);
    console.log(`Key: ${key}, Value: ${value}`);
  }

  // Listen for storage changes from other tabs/windows
  window.addEventListener('storage', (event) => {
    console.log(`Storage changed: ${event.key} changed from ${event.oldValue} to ${event.newValue}`);
    console.log(`Affected storage area: ${event.storageArea === localStorage ? 'localStorage' : 'sessionStorage'}`);
    console.log(`Source URL: ${event.url}`);
  });
}

sessionStorage for Temporary Data

// sessionStorage works identically but is scoped to the tab session
// Data survives page refreshes but not tab close/open

// Store form state before navigation
function saveFormState() {
  const formData = {
    name: document.getElementById('name').value,
    email: document.getElementById('email').value,
    message: document.getElementById('message').value
  };
  sessionStorage.setItem('formDraft', JSON.stringify(formData));
}

// Restore form state on page load
function restoreFormState() {
  const saved = sessionStorage.getItem('formDraft');
  if (saved) {
    const formData = JSON.parse(saved);
    document.getElementById('name').value = formData.name || '';
    document.getElementById('email').value = formData.email || '';
    document.getElementById('message').value = formData.message || '';
  }
}

// Attach save on navigation
window.addEventListener('beforeunload', saveFormState);
document.addEventListener('DOMContentLoaded', restoreFormState);

Best Practices for Web Storage

Canvas API

The HTML5 Canvas Element

The Canvas API provides a programmatic drawing surface for 2D graphics, enabling pixel-level manipulation, shapes, text rendering, image compositing, and animation. It is hardware-accelerated in modern browsers and forms the foundation of web-based games, data visualizations, image editors, and generative art. The API operates through a drawing context obtained from a <canvas> element.

Drawing Fundamentals

// HTML: 

const canvas = document.getElementById('myCanvas');
// Check for canvas support
if (!canvas.getContext) {
  console.error('Canvas not supported');
} else {
  const ctx = canvas.getContext('2d');

  // ---- Basic Shapes ----
  // Rectangle with fill
  ctx.fillStyle = 'rgb(70, 130, 180)';
  ctx.fillRect(50, 50, 200, 100);

  // Rectangle with stroke (outline)
  ctx.strokeStyle = 'rgb(200, 50, 50)';
  ctx.lineWidth = 4;
  ctx.strokeRect(300, 50, 200, 100);

  // Clear a region
  ctx.clearRect(100, 70, 100, 60);

  // ---- Paths and Lines ----
  ctx.beginPath();
  ctx.moveTo(50, 200);
  ctx.lineTo(200, 300);
  ctx.lineTo(350, 220);
  ctx.lineTo(500, 350);
  ctx.strokeStyle = '#333';
  ctx.lineWidth = 3;
  ctx.lineCap = 'round';
  ctx.lineJoin = 'round';
  ctx.stroke();

  // ---- Circles and Arcs ----
  ctx.beginPath();
  ctx.arc(400, 400, 80, 0, Math.PI * 2, false); // Full circle
  ctx.fillStyle = 'rgba(255, 200, 50, 0.7)';
  ctx.fill();
  ctx.strokeStyle = '#000';
  ctx.lineWidth = 2;
  ctx.stroke();

  // ---- Text Rendering ----
  ctx.font = 'bold 36px "Segoe UI", sans-serif';
  ctx.fillStyle = '#222';
  ctx.textAlign = 'center';
  ctx.textBaseline = 'middle';
  ctx.fillText('Canvas API', 400, 480);

  // Measure text width for layout calculations
  const textMetrics = ctx.measureText('Canvas API');
  console.log('Text width:', textMetrics.width);

  // ---- Gradients ----
  const gradient = ctx.createLinearGradient(600, 50, 750, 150);
  gradient.addColorStop(0, '#ff6b6b');
  gradient.addColorStop(0.5, '#feca57');
  gradient.addColorStop(1, '#48dbfb');
  ctx.fillStyle = gradient;
  ctx.fillRect(600, 50, 150, 100);

  // ---- Images ----
  const img = new Image();
  img.onload = () => {
    // Draw image with scaling
    ctx.drawImage(img, 600, 200, 150, 100);
    // Draw a portion of the image: source x, y, w, h, dest x, y, w, h
    // ctx.drawImage(img, 10, 10, 50, 50, 600, 350, 100, 100);
  };
  img.src = 'https://example.com/sample-image.jpg';
}

Animation Loop

const canvas = document.getElementById('animationCanvas');
const ctx = canvas.getContext('2d');

let ballX = 50;
let ballY = 50;
let velocityX = 3;
let velocityY = 2;
const ballRadius = 15;

function animate() {
  // Clear the entire canvas each frame
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  // Update position
  ballX += velocityX;
  ballY += velocityY;

  // Bounce off walls
  if (ballX + ballRadius > canvas.width || ballX - ballRadius < 0) {
    velocityX = -velocityX;
  }
  if (ballY + ballRadius > canvas.height || ballY - ballRadius < 0) {
    velocityY = -velocityY;
  }

  // Draw the ball
  ctx.beginPath();
  ctx.arc(ballX, ballY, ballRadius, 0, Math.PI * 2);
  ctx.fillStyle = 'crimson';
  ctx.fill();
  ctx.strokeStyle = '#333';
  ctx.stroke();

  // Continue the animation loop
  requestAnimationFrame(animate);
}

// Start the animation
requestAnimationFrame(animate);

Best Practices for Canvas

Web Workers API

Enabling Multithreading in the Browser

Web Workers allow you to run JavaScript code in background threads, completely independent of the main UI thread. This prevents heavy computation from blocking user interactions, scrolling, and animations. Workers communicate with the main thread through a message-passing system using postMessage() and onmessage event handlers. There are three types: dedicated workers (single tab), shared workers (multiple tabs/browsing contexts), and service workers (network proxy and offline cache).

Dedicated Worker Implementation

First, create the worker script file heavy-computation.js:

// heavy-computation.js — This runs in a separate thread
self.onmessage = function(event) {
  const data = event.data;
  console.log('Worker received:', data);

  // Perform CPU-intensive work without blocking the UI
  const startTime = performance.now();

  // Example: Calculate prime numbers up to a limit
  function findPrimes(limit) {
    const primes = [];
    const sieve = new Uint8Array(limit + 1);

    for (let i = 2; i <= limit; i++) {
      if (sieve[i] === 0) {
        primes.push(i);
        for (let j = i * i; j <= limit; j += i) {
          sieve[j] = 1;
        }
      }
    }
    return primes;
  }

  const primes = findPrimes(data.limit || 100000);
  const endTime = performance.now();

  // Send result back to the main thread
  self.postMessage({
    primesCount: primes.length,
    lastTenPrimes: primes.slice(-10),
    computationTime: endTime - startTime
  });
};

// Optional: handle errors in the worker
self.onerror = function(error) {
  console.error('Worker error:', error.message);
};

Now, the main thread code that creates and communicates with the worker:

// Main thread (app.js)
// Check for Worker support
if (window.Worker) {
  const worker = new Worker('heavy-computation.js');

  // Listen for messages from the worker
  worker.onmessage = function(event) {
    const result = event.data;
    console.log(`Found ${result.primesCount} primes`);
    console.log(`Last 10 primes: ${result.lastTenPrimes.join(', ')}`);
    console.log(`Computation took ${result.computationTime.toFixed(2)} ms in worker`);
    document.getElementById('result').textContent =
      `Found ${result.primesCount} primes in ${result.computationTime.toFixed(0)} ms`;
  };

  // Handle worker errors
  worker.onerror = function(error) {
    console.error('Worker error on line', error.lineno, ':', error.message);
    document.getElementById('result').textContent = 'Computation failed';
  };

  // Send data to the worker
  document.getElementById('calculate-btn').addEventListener('click', () => {
    const limit = parseInt(document.getElementById('limit-input').value, 10) || 50000;
    document.getElementById('result').textContent = 'Calculating...';
    worker.postMessage({ limit: limit });
  });

  // Terminate the worker when done (or on page unload)
  window.addEventListener('beforeunload', () => {
    worker.terminate();
  });
} else {
  console.error('Web Workers are not supported in this browser.');
  // Implement fallback: run computation on main thread with progress indication
}

Transferring Data Efficiently

// Transferring ownership of large buffers for zero-copy performance
const largeArrayBuffer = new ArrayBuffer(1024 * 1024 * 10); // 10 MB

const worker = new Worker('data-processor.js');

// Transfer the buffer ownership to the worker (main thread loses access)
worker.postMessage({ buffer: largeArrayBuffer, metadata: { id: 123 } },
  [largeArrayBuffer]); // List of transferable objects

// After transfer, largeArrayBuffer.byteLength === 0 in main thread
console.log('Buffer transferred, main thread byteLength:', largeArrayBuffer.byteLength);

Best Practices for Web Workers

History API

Single-Page Application Routing

The History API enables manipulation of the browser session history, allowing developers to change the URL displayed in the address bar without triggering a full page reload. This is essential for single-page applications (SPAs) where navigation happens client-side. The API provides pushState(), replaceState(), and the popstate event for handling back/forward navigation.

Complete SPA Router Example

// Simple client-side router using History API
class SPARouter {
  constructor() {
    this.routes = new Map();
    this.currentRoute = null;

    // Bind to popstate for back/forward navigation
    window.addEventListener('popstate', (event) => {
      this.handleRoute(window.location.pathname, event.state || {});
    });

    // Intercept link clicks for internal navigation
    document.addEventListener('click', (event) => {
      const link = event.target.closest('a[data-spa-link]');
      if (link && link.href.startsWith(window.location.origin)) {
        event.preventDefault();
        const path = link.getAttribute('href');
        const state = { fromLink: true, timestamp: Date.now() };
        this.navigate(path, state);
      }
    });
  }

  register(path, handler) {
    this.routes.set(path, handler);
  }

  navigate(url, state = {}, title = '') {
    // Push new state onto history stack
    window.history.pushState(state, title, url);
    this.handleRoute(url, state);
  }

  replace(url, state = {}, title = '') {
    // Replace current state without adding to history stack
    window.history.replaceState(state, title, url);
    this.handleRoute(url, state);
  }

  handleRoute(path, state) {
    const handler = this.routes.get(path);
    const container = document.getElementById('app-content');

    if (handler) {
      this.currentRoute = path;
      handler({ container, state, path });
    } else {
      // Fallback for unknown routes
      container.innerHTML = '

404 - Page Not Found

'; } } init() { // Handle the initial page load const initialState = { initialLoad: true }; window.history.replaceState(initialState, '', window.location.pathname); this.handleRoute(window.location.pathname, initialState); } } // Usage example const router = new SPARouter(); router.register('/', ({ container }) => { container.innerHTML = `

Home Page

Welcome! This content is rendered client-side without a page reload.

Go to About `; }); router.register('/about', ({ container }) => { container.innerHTML = `

About Us

We are a company that builds SPAs using the History API.

Back to Home `; }); router.register('/contact', ({ container }) => { container.innerHTML = `

Contact

Email us at info@example.com

Back to Home `; }); router.init();

Programmatic Back/Forward Navigation

// Navigate programmatically
document.getElementById('back-btn').addEventListener('click', () => {
  window.history.back(); // Equivalent to user clicking browser back button
});

document.getElementById('forward-btn').addEventListener('click', () => {
  window.history.forward();
});

document.getElementById('go-btn').addEventListener('click', () => {
  const steps = parseInt(document.getElementById('steps-input').value, 10);
  window.history.go(steps); // Negative for back, positive for forward
});

// Check history length
console.log(`History entries: ${window.history.length}`);

Best Practices for History API

Drag and Drop API

Native Drag-and-Drop Capabilities

The Drag and Drop API enables elements to be dragged within a page or between applications. It provides a complete event-driven system with drag initiation, drag-over feedback, and drop handling. The API supports both intra-page element reordering and file imports from the operating system desktop.

Complete Drag-and-Drop Implementation

// HTML structure for a sortable list:
// 
    //
  • Item 1
  • //
  • Item 2
  • //
  • Item 3
  • //
const sortableList = document.getElementById('sortable-list'); let draggedElement = null; let draggedOverElement = null; // ---- Drag Start ---- sortableList.addEventListener('dragstart', (event) => { draggedElement = event.target; // Make the dragged element semi-transparent event.target.style.opacity = '0.4'; // Store data to be transferred event.dataTransfer.setData('text/plain', event.target.dataset.id); event.dataTransfer.effectAllowed = 'move'; // Set custom drag image (optional) const dragImage = event.target.cloneNode(true); dragImage.style.opacity = '0.8'; dragImage.style.position = 'absolute'; dragImage.style.top = '-9999px'; document.body.appendChild(dragImage); event.dataTransfer.setDragImage(dragImage, 20, 20); // Clean up the drag image clone after a short delay setTimeout(() => document.body.removeChild(dragImage), 0); }); // ---- Drag Over (allow drop) ---- sortableList.addEventListener('dragover', (event) => { event.preventDefault(); // Required to allow a drop event.dataTransfer.dropEffect = 'move'; draggedOverElement = event.target.closest('li'); }); // ---- Drag Enter (visual feedback) ---- sortableList.addEventListener('dragenter', (event) => { event.preventDefault(); const targetLi = event.target.closest('li'); if (targetLi && targetLi !== draggedElement) { targetLi.style.borderTop = '3px solid #2196F3'; } }); // ---- Drag Leave ---- sortableList.addEventListener('dragleave', (event) => { const targetLi = event.target.closest('li'); if (targetLi) { targetLi.style.borderTop = ''; } }); // ---- Drop ---- sortableList.addEventListener('drop', (event) => { event.preventDefault(); const targetLi = event.target.closest('li'); if (targetLi && draggedElement !== targetLi) { // Reorder elements in the DOM const parent = draggedElement.parentNode; const referenceNode = targetLi; if (draggedElement.compareDocumentPosition(referenceNode) & Node.DOCUMENT_POSITION_FOLLOWING) { // draggedElement is before referenceNode parent.insertBefore(draggedElement, referenceNode.nextSibling); } else { parent.insertBefore(draggedElement, referenceNode); } // Reset styles targetLi.style.borderTop = ''; } // Reset the dragged element draggedElement.style.opacity = '1'; draggedElement = null; }); // ---- Drag End (cleanup) ---- sortableList.addEventListener('dragend', (event) => { event.target.style.opacity = '1'; // Remove any remaining visual indicators const allItems = sortableList.querySelectorAll('li'); allItems.forEach(item => item.style.borderTop = ''); draggedElement = null; draggedOverElement = null; });

File Drop from Desktop

const dropZone = document.getElementById('file-drop-zone');

// Prevent default to allow drops
dropZone.addEventListener('dragover', (event) => {
  event.preventDefault();
  event.dataTransfer.dropEffect = 'copy';
  dropZone.classList.add('drag-over');
});

dropZone.addEventListener('dragleave', () => {
  dropZone.classList.remove('drag-over');
});

dropZone.addEventListener('drop', (event) => {
  event.preventDefault();
  dropZone.classList.remove('drag-over');

  const files = event.dataTransfer.files;
  console.log(`Dropped ${files.length} file(s)`);

  for (let i = 0; i < files.length; i++) {
    const file = files[i];
    console.log(`File: ${file.name}, Size: ${file.size} bytes, Type: ${file.type}`);

    // Read file contents (demonstration with text files)
    if (file.type.startsWith('text/')) {
      const reader = new FileReader();
      reader.onload = (e) => {
        console.log(`Contents of ${file.name}:`, e.target.result.substring(0, 200));
      };
      reader.readAsText(file);
    }
  }
});

Best Practices for Drag and Drop

File API

Reading Files Client-Side

The File API provides the ability to read file contents and access file metadata entirely on the client side, without uploading to a server. It works in conjunction with the <input type="file"> element and the Drag and Drop API. The API exposes File, FileList, FileReader, and Blob interfaces for handling binary data.

Complete File Reading Example

// HTML: 
//       
//
const fileInput = document.getElementById('file-input'); const previewContainer = document.getElementById('preview-container'); const fileInfo = document.getElementById('file-info'); fileInput.addEventListener('change', (event) => { const files = event.target.files; // FileList object fileInfo.innerHTML = ''; previewContainer.innerHTML = ''; if (files.length === 0) return; // Display file metadata let infoHTML = `

Selected ${files.length} file(s)

`; for (let i = 0; i < files.length; i++) { const file = files[i]; const lastModified = new Date(file.lastModified).toLocaleString(); infoHTML += `

${file.name}
Size: ${formatFileSize(file.size)}
Type: ${file.type || 'unknown'}
Last modified: ${lastModified}

`; } fileInfo.innerHTML = infoHTML; // Process each file for (let i = 0; i < files.length; i++) { processFile(files[i]); } }); function formatFileSize(bytes) { if (bytes < 1024) return bytes + ' bytes'; if (bytes < 1048576) return (bytes / 1024).toFixed(1) + ' KB'; return (bytes / 1048576).toFixed(2) + ' MB'; } function processFile(file) { const reader = new FileReader();

🚀 Need a reliable AI agent for your project?

Deploy Hermes Agent in 10 minutes. Managed hosting, zero DevOps.

Get Started — $23.99/mo
← Back to all articles