DEV Community

Cover image for 15 Advanced Web APIs Every Modern Developer Must Master πŸš€
CodewithEVILXD
CodewithEVILXD

Posted on

15 Advanced Web APIs Every Modern Developer Must Master πŸš€

Modern web development demands more than just knowing HTML, CSS, and JavaScript. The browser provides powerful APIs that can transform your applications from basic to extraordinary. This comprehensive guide covers 15 essential Web APIs with production-ready examples, best practices, and real-world implementations.


Table of Contents

  1. IndexedDB API
  2. Web Workers API
  3. WebRTC API
  4. Intersection Observer API
  5. Web Notifications API
  6. Web Speech API
  7. WebSocket API
  8. Service Worker API
  9. Resize Observer API
  10. Broadcast Channel API
  11. WebGL API
  12. Payment Request API
  13. Credential Management API
  14. Battery Status API
  15. Screen Wake Lock API

1. IndexedDB API - Client-Side Database Powerhouse πŸ’Ύ

IndexedDB is a low-level API for storing significant amounts of structured data, including files and blobs. Unlike localStorage's 5-10MB limit, IndexedDB can store hundreds of megabytes or more.

Why Use IndexedDB?

  • Large Storage: Handles GBs of data
  • Indexed Queries: Fast data retrieval with indexes
  • Asynchronous: Non-blocking operations
  • Transactional: ACID-compliant database operations

Production-Ready Implementation

class IndexedDBManager {
  constructor(dbName, version = 1) {
    this.dbName = dbName;
    this.version = version;
    this.db = null;
  }

  async init() {
    return new Promise((resolve, reject) => {
      const request = indexedDB.open(this.dbName, this.version);

      request.onerror = () => reject(request.error);
      request.onsuccess = () => {
        this.db = request.result;
        resolve(this.db);
      };

      request.onupgradeneeded = (event) => {
        const db = event.target.result;

        // Create object stores
        if (!db.objectStoreNames.contains('users')) {
          const userStore = db.createObjectStore('users', { keyPath: 'id', autoIncrement: true });
          userStore.createIndex('email', 'email', { unique: true });
          userStore.createIndex('name', 'name', { unique: false });
        }

        if (!db.objectStoreNames.contains('posts')) {
          const postStore = db.createObjectStore('posts', { keyPath: 'id', autoIncrement: true });
          postStore.createIndex('userId', 'userId', { unique: false });
          postStore.createIndex('timestamp', 'timestamp', { unique: false });
        }
      };
    });
  }

  async add(storeName, data) {
    const transaction = this.db.transaction([storeName], 'readwrite');
    const store = transaction.objectStore(storeName);

    return new Promise((resolve, reject) => {
      const request = store.add(data);
      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }

  async get(storeName, key) {
    const transaction = this.db.transaction([storeName], 'readonly');
    const store = transaction.objectStore(storeName);

    return new Promise((resolve, reject) => {
      const request = store.get(key);
      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }

  async getByIndex(storeName, indexName, value) {
    const transaction = this.db.transaction([storeName], 'readonly');
    const store = transaction.objectStore(storeName);
    const index = store.index(indexName);

    return new Promise((resolve, reject) => {
      const request = index.get(value);
      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }

  async getAll(storeName) {
    const transaction = this.db.transaction([storeName], 'readonly');
    const store = transaction.objectStore(storeName);

    return new Promise((resolve, reject) => {
      const request = store.getAll();
      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }

  async update(storeName, data) {
    const transaction = this.db.transaction([storeName], 'readwrite');
    const store = transaction.objectStore(storeName);

    return new Promise((resolve, reject) => {
      const request = store.put(data);
      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }

  async delete(storeName, key) {
    const transaction = this.db.transaction([storeName], 'readwrite');
    const store = transaction.objectStore(storeName);

    return new Promise((resolve, reject) => {
      const request = store.delete(key);
      request.onsuccess = () => resolve();
      request.onerror = () => reject(request.error);
    });
  }
}

// Usage Example
const dbManager = new IndexedDBManager('MyApp');

await dbManager.init();

// Add user
const userId = await dbManager.add('users', {
  name: 'John Doe',
  email: 'john@example.com',
  role: 'admin',
  createdAt: new Date()
});

// Get user by email
const user = await dbManager.getByIndex('users', 'email', 'john@example.com');

// Update user
await dbManager.update('users', {
  ...user,
  role: 'superadmin'
});
Enter fullscreen mode Exit fullscreen mode

Real-World Use Cases

  • Offline-First Applications: Store data locally, sync when online
  • Progressive Web Apps: Cache API responses and user data
  • Large Dataset Management: Handle complex queries client-side
  • Media Storage: Store images, videos, and audio files

Learn More


2. Web Workers API - Multithreading in JavaScript ⚑

Web Workers enable true parallel processing in JavaScript, running scripts in background threads without blocking the UI.

The Problem Web Workers Solve

JavaScript is single-threaded. Heavy computations freeze the UI, creating poor user experiences. Web Workers solve this by offloading work to separate threads.

Advanced Implementation with Worker Pool

// worker-pool.js - Reusable Worker Pool Manager
class WorkerPool {
  constructor(workerScript, poolSize = navigator.hardwareConcurrency || 4) {
    this.workerScript = workerScript;
    this.poolSize = poolSize;
    this.workers = [];
    this.queue = [];
    this.activeWorkers = new Set();

    this.initializePool();
  }

  initializePool() {
    for (let i = 0; i < this.poolSize; i++) {
      const worker = new Worker(this.workerScript);
      worker.id = i;
      this.workers.push(worker);
    }
  }

  async execute(data, transferable = []) {
    return new Promise((resolve, reject) => {
      const task = { data, transferable, resolve, reject };

      const availableWorker = this.workers.find(w => !this.activeWorkers.has(w));

      if (availableWorker) {
        this.runTask(availableWorker, task);
      } else {
        this.queue.push(task);
      }
    });
  }

  runTask(worker, task) {
    this.activeWorkers.add(worker);

    const messageHandler = (e) => {
      worker.removeEventListener('message', messageHandler);
      worker.removeEventListener('error', errorHandler);
      this.activeWorkers.delete(worker);

      task.resolve(e.data);
      this.processQueue();
    };

    const errorHandler = (e) => {
      worker.removeEventListener('message', messageHandler);
      worker.removeEventListener('error', errorHandler);
      this.activeWorkers.delete(worker);

      task.reject(e);
      this.processQueue();
    };

    worker.addEventListener('message', messageHandler);
    worker.addEventListener('error', errorHandler);
    worker.postMessage(task.data, task.transferable);
  }

  processQueue() {
    if (this.queue.length === 0) return;

    const availableWorker = this.workers.find(w => !this.activeWorkers.has(w));
    if (availableWorker) {
      const task = this.queue.shift();
      this.runTask(availableWorker, task);
    }
  }

  terminate() {
    this.workers.forEach(worker => worker.terminate());
    this.workers = [];
    this.queue = [];
    this.activeWorkers.clear();
  }
}

// image-processor-worker.js
self.addEventListener('message', (e) => {
  const { imageData, operation } = e.data;

  switch (operation) {
    case 'grayscale':
      const grayData = applyGrayscale(imageData);
      self.postMessage({ result: grayData }, [grayData.data.buffer]);
      break;

    case 'blur':
      const blurData = applyBlur(imageData, e.data.radius);
      self.postMessage({ result: blurData }, [blurData.data.buffer]);
      break;

    case 'brighten':
      const brightData = applyBrighten(imageData, e.data.amount);
      self.postMessage({ result: brightData }, [brightData.data.buffer]);
      break;
  }
});

function applyGrayscale(imageData) {
  const data = imageData.data;
  for (let i = 0; i < data.length; i += 4) {
    const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
    data[i] = data[i + 1] = data[i + 2] = avg;
  }
  return imageData;
}

function applyBlur(imageData, radius = 5) {
  // Implement Gaussian blur algorithm
  // ... (implementation details)
  return imageData;
}

function applyBrighten(imageData, amount = 20) {
  const data = imageData.data;
  for (let i = 0; i < data.length; i += 4) {
    data[i] = Math.min(255, data[i] + amount);
    data[i + 1] = Math.min(255, data[i + 1] + amount);
    data[i + 2] = Math.min(255, data[i + 2] + amount);
  }
  return imageData;
}

// Usage in main thread
const workerPool = new WorkerPool('image-processor-worker.js', 4);

async function processImage(canvas, operation) {
  const ctx = canvas.getContext('2d');
  const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);

  try {
    const result = await workerPool.execute(
      { imageData, operation, radius: 5, amount: 30 },
      [imageData.data.buffer] // Transfer ownership for better performance
    );

    ctx.putImageData(result.result, 0, 0);
  } catch (error) {
    console.error('Image processing failed:', error);
  }
}

// Process multiple images concurrently
const images = document.querySelectorAll('.image-to-process');
await Promise.all(
  Array.from(images).map(img => 
    processImage(img, 'grayscale')
  )
);
Enter fullscreen mode Exit fullscreen mode

Performance Comparison

Task Main Thread Web Worker Improvement
Image Processing (1920x1080) ~850ms (UI frozen) ~220ms (UI responsive) 74% faster perceived
JSON Parsing (50MB) ~2300ms ~2100ms UI remains smooth
Complex Calculations Blocks rendering Background execution Infinite UX improvement

Best Practices

  • Use Transferable Objects: Transfer array buffers instead of copying
  • Implement Worker Pools: Reuse workers for better performance
  • Handle Errors Gracefully: Always implement error handlers
  • Consider SharedArrayBuffer: For data sharing between workers

Learn More


3. WebRTC API - Real-Time Communication πŸ“Ή

WebRTC (Web Real-Time Communication) enables peer-to-peer audio, video, and data sharing without plugins or intermediary servers.

Complete Video Chat Implementation

class VideoChat {
  constructor(localVideoEl, remoteVideoEl, signalingServer) {
    this.localVideo = localVideoEl;
    this.remoteVideo = remoteVideoEl;
    this.signalingServer = signalingServer;
    this.peerConnection = null;
    this.localStream = null;

    this.configuration = {
      iceServers: [
        { urls: 'stun:stun.l.google.com:19302' },
        { urls: 'stun:stun1.l.google.com:19302' },
        {
          urls: 'turn:your-turn-server.com:3478',
          username: 'user',
          credential: 'pass'
        }
      ]
    };

    this.setupSignaling();
  }

  async initialize() {
    try {
      // Get local media stream
      this.localStream = await navigator.mediaDevices.getUserMedia({
        video: {
          width: { ideal: 1280 },
          height: { ideal: 720 },
          frameRate: { ideal: 30 }
        },
        audio: {
          echoCancellation: true,
          noiseSuppression: true,
          autoGainControl: true
        }
      });

      this.localVideo.srcObject = this.localStream;

      // Create peer connection
      this.peerConnection = new RTCPeerConnection(this.configuration);

      // Add local tracks to peer connection
      this.localStream.getTracks().forEach(track => {
        this.peerConnection.addTrack(track, this.localStream);
      });

      // Handle incoming tracks
      this.peerConnection.ontrack = (event) => {
        this.remoteVideo.srcObject = event.streams[0];
      };

      // Handle ICE candidates
      this.peerConnection.onicecandidate = (event) => {
        if (event.candidate) {
          this.signalingServer.send({
            type: 'ice-candidate',
            candidate: event.candidate
          });
        }
      };

      // Monitor connection state
      this.peerConnection.onconnectionstatechange = () => {
        console.log('Connection state:', this.peerConnection.connectionState);

        if (this.peerConnection.connectionState === 'disconnected') {
          this.handleDisconnection();
        }
      };

      // Monitor ICE connection state
      this.peerConnection.oniceconnectionstatechange = () => {
        console.log('ICE state:', this.peerConnection.iceConnectionState);
      };

    } catch (error) {
      console.error('Failed to initialize video chat:', error);
      throw error;
    }
  }

  setupSignaling() {
    this.signalingServer.on('offer', async (offer) => {
      await this.peerConnection.setRemoteDescription(new RTCSessionDescription(offer));
      const answer = await this.peerConnection.createAnswer();
      await this.peerConnection.setLocalDescription(answer);

      this.signalingServer.send({
        type: 'answer',
        answer: answer
      });
    });

    this.signalingServer.on('answer', async (answer) => {
      await this.peerConnection.setRemoteDescription(new RTCSessionDescription(answer));
    });

    this.signalingServer.on('ice-candidate', async (candidate) => {
      await this.peerConnection.addIceCandidate(new RTCIceCandidate(candidate));
    });
  }

  async createOffer() {
    const offer = await this.peerConnection.createOffer({
      offerToReceiveAudio: true,
      offerToReceiveVideo: true
    });

    await this.peerConnection.setLocalDescription(offer);

    this.signalingServer.send({
      type: 'offer',
      offer: offer
    });
  }

  toggleAudio(enabled) {
    this.localStream.getAudioTracks().forEach(track => {
      track.enabled = enabled;
    });
  }

  toggleVideo(enabled) {
    this.localStream.getVideoTracks().forEach(track => {
      track.enabled = enabled;
    });
  }

  async switchCamera() {
    const videoTrack = this.localStream.getVideoTracks()[0];
    const currentFacingMode = videoTrack.getSettings().facingMode;

    videoTrack.stop();

    const newStream = await navigator.mediaDevices.getUserMedia({
      video: { facingMode: currentFacingMode === 'user' ? 'environment' : 'user' }
    });

    const newVideoTrack = newStream.getVideoTracks()[0];
    const sender = this.peerConnection.getSenders().find(s => s.track.kind === 'video');

    sender.replaceTrack(newVideoTrack);
    this.localStream.removeTrack(videoTrack);
    this.localStream.addTrack(newVideoTrack);
    this.localVideo.srcObject = this.localStream;
  }

  async shareScreen() {
    try {
      const screenStream = await navigator.mediaDevices.getDisplayMedia({
        video: { cursor: 'always' },
        audio: false
      });

      const screenTrack = screenStream.getVideoTracks()[0];
      const sender = this.peerConnection.getSenders().find(s => s.track.kind === 'video');

      sender.replaceTrack(screenTrack);

      // Restore camera when screen sharing stops
      screenTrack.onended = () => {
        const videoTrack = this.localStream.getVideoTracks()[0];
        sender.replaceTrack(videoTrack);
      };

    } catch (error) {
      console.error('Screen sharing failed:', error);
    }
  }

  getStats() {
    return this.peerConnection.getStats();
  }

  handleDisconnection() {
    console.log('Peer disconnected. Attempting reconnection...');
    // Implement reconnection logic
  }

  disconnect() {
    if (this.localStream) {
      this.localStream.getTracks().forEach(track => track.stop());
    }

    if (this.peerConnection) {
      this.peerConnection.close();
    }

    this.localVideo.srcObject = null;
    this.remoteVideo.srcObject = null;
  }
}

// Usage
const videoChat = new VideoChat(
  document.getElementById('local-video'),
  document.getElementById('remote-video'),
  signalingServerConnection
);

await videoChat.initialize();
await videoChat.createOffer();

// Control buttons
document.getElementById('mute-btn').addEventListener('click', () => {
  videoChat.toggleAudio(false);
});

document.getElementById('share-screen-btn').addEventListener('click', () => {
  videoChat.shareScreen();
});
Enter fullscreen mode Exit fullscreen mode

Applications

  • Video Conferencing: Zoom, Google Meet alternatives
  • Live Streaming: Twitch-like platforms
  • Online Gaming: Real-time multiplayer games
  • IoT Communication: Device-to-device communication
  • File Sharing: P2P file transfer without servers

Learn More


4. Intersection Observer API - Efficient Scroll Detection 🎯

The Intersection Observer API provides a way to asynchronously observe changes in the intersection of a target element with an ancestor element or the viewport.

Advanced Implementation with Performance Optimization

class LazyLoadManager {
  constructor(options = {}) {
    this.options = {
      root: options.root || null,
      rootMargin: options.rootMargin || '50px',
      threshold: options.threshold || 0.1,
      unobserveOnLoad: options.unobserveOnLoad !== false
    };

    this.observer = null;
    this.observed = new WeakMap();
    this.initialize();
  }

  initialize() {
    this.observer = new IntersectionObserver(
      (entries) => this.handleIntersection(entries),
      this.options
    );
  }

  handleIntersection(entries) {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        const element = entry.target;
        const callback = this.observed.get(element);

        if (callback) {
          callback(element, entry);

          if (this.options.unobserveOnLoad) {
            this.unobserve(element);
          }
        }
      }
    });
  }

  observe(element, callback) {
    this.observed.set(element, callback);
    this.observer.observe(element);
  }

  unobserve(element) {
    this.observed.delete(element);
    this.observer.unobserve(element);
  }

  disconnect() {
    this.observer.disconnect();
    this.observed = new WeakMap();
  }
}

// Image Lazy Loading with Progressive Enhancement
class ImageLazyLoader extends LazyLoadManager {
  constructor(options) {
    super(options);
    this.loadedImages = new Set();
  }

  loadImage(img) {
    return new Promise((resolve, reject) => {
      // Add loading placeholder
      img.classList.add('loading');

      const src = img.dataset.src;
      const srcset = img.dataset.srcset;

      // Create a temporary image to preload
      const tempImg = new Image();

      tempImg.onload = () => {
        img.src = src;
        if (srcset) img.srcset = srcset;

        img.classList.remove('loading');
        img.classList.add('loaded');

        this.loadedImages.add(img);
        resolve(img);
      };

      tempImg.onerror = () => {
        img.classList.remove('loading');
        img.classList.add('error');
        reject(new Error(`Failed to load image: ${src}`));
      };

      tempImg.src = src;
    });
  }

  observeImages(selector = 'img[data-src]') {
    const images = document.querySelectorAll(selector);

    images.forEach(img => {
      this.observe(img, async (element) => {
        try {
          await this.loadImage(element);
        } catch (error) {
          console.error(error);
        }
      });
    });
  }
}

// Infinite Scroll Implementation
class InfiniteScroll extends LazyLoadManager {
  constructor(options) {
    super({
      ...options,
      rootMargin: '200px',
      threshold: 0
    });

    this.loading = false;
    this.page = 1;
    this.hasMore = true;
  }

  async loadMore(fetchFunction) {
    if (this.loading || !this.hasMore) return;

    this.loading = true;

    try {
      const data = await fetchFunction(this.page);

      if (data && data.length > 0) {
        this.renderData(data);
        this.page++;
      } else {
        this.hasMore = false;
      }
    } catch (error) {
      console.error('Failed to load more:', error);
    } finally {
      this.loading = false;
    }
  }

  renderData(data) {
    // Implement your rendering logic
    const container = document.getElementById('content-container');

    data.forEach(item => {
      const element = this.createItemElement(item);
      container.appendChild(element);
    });
  }

  createItemElement(item) {
    const div = document.createElement('div');
    div.className = 'item';
    div.innerHTML = `
      <h3>${item.title}</h3>
      <p>${item.description}</p>
    `;
    return div;
  }

  init(sentinel, fetchFunction) {
    this.observe(sentinel, () => {
      this.loadMore(fetchFunction);
    });
  }
}

// Animate on Scroll
class ScrollAnimator extends LazyLoadManager {
  constructor(options) {
    super({
      ...options,
      threshold: 0.2,
      unobserveOnLoad: true
    });
  }

  animateElements(selector = '.animate-on-scroll') {
    const elements = document.querySelectorAll(selector);

    elements.forEach(el => {
      this.observe(el, (element, entry) => {
        const animationClass = element.dataset.animation || 'fade-in';
        const delay = element.dataset.delay || 0;

        setTimeout(() => {
          element.classList.add(animationClass, 'animated');
        }, delay);
      });
    });
  }
}

// Usage Examples

// 1. Image Lazy Loading
const imageLazyLoader = new ImageLazyLoader({
  rootMargin: '100px',
  threshold: 0.01
});
imageLazyLoader.observeImages();

// 2. Infinite Scroll
const infiniteScroll = new InfiniteScroll();
const sentinel = document.getElementById('scroll-sentinel');

infiniteScroll.init(sentinel, async (page) => {
  const response = await fetch(`/api/posts?page=${page}`);
  return response.json();
});

// 3. Scroll Animations
const animator = new ScrollAnimator({ threshold: 0.3 });
animator.animateElements('.fade-in-element');

// 4. Track Visibility for Analytics
const analyticsObserver = new LazyLoadManager({ threshold: 0.5 });
document.querySelectorAll('[data-track-view]').forEach(el => {
  analyticsObserver.observe(el, (element) => {
    const eventName = element.dataset.trackView;
    analytics.track('element_viewed', { element: eventName });
  });
});
Enter fullscreen mode Exit fullscreen mode

CSS for Animations

/* Loading states */
img.loading {
  background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
  background-size: 200% 100%;
  animation: loading 1.5s infinite;
}

@keyframes loading {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

/* Scroll animations */
.animate-on-scroll {
  opacity: 0;
  transform: translateY(30px);
  transition: opacity 0.6s ease, transform 0.6s ease;
}

.animate-on-scroll.animated {
  opacity: 1;
  transform: translateY(0);
}

.fade-in {
  animation: fadeIn 0.6s ease forwards;
}

@keyframes fadeIn {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}
Enter fullscreen mode Exit fullscreen mode

Performance Benefits

  • No Scroll Events: Eliminates expensive scroll event listeners
  • Asynchronous: Non-blocking, runs off the main thread
  • Precise Control: Multiple thresholds and margins
  • Battery Efficient: Only fires when needed

Learn More


5. Web Notifications API - Engage Users Effectively πŸ””

The Notifications API allows web pages to send system notifications that appear outside the page, even when the browser is minimized.

Advanced Notification System

class NotificationManager {
  constructor() {
    this.permission = Notification.permission;
    this.queue = [];
    this.activeNotifications = new Map();
  }

  async requestPermission() {
    if (this.permission === 'granted') {
      return true;
    }

    if (this.permission === 'denied') {
      console.warn('Notification permission denied');
      return false;
    }

    try {
      const result = await Notification.requestPermission();
      this.permission = result;
      return result === 'granted';
    } catch (error) {
      console.error('Failed to request notification permission:', error);
      return false;
    }
  }

  async send(title, options = {}) {
    if (this.permission !== 'granted') {
      const granted = await this.requestPermission();
      if (!granted) return null;
    }

    const defaultOptions = {
      icon: '/icon.png',
      badge: '/badge.png',
      vibrate: [200, 100, 200],
      timestamp: Date.now(),
      requireInteraction: false,
      silent: false,
      ...options
    };

    try {
      const notification = new Notification(title, defaultOptions);

      // Track active notifications
      const id = options.tag || `notif-${Date.now()}`;
      this.activeNotifications.set(id, notification);

      // Event handlers
      notification.onclick = (event) => {
        event.preventDefault();
        window.focus();

        if (options.onClick) {
          options.onClick(event);
        }

        if (options.data?.url) {
          window.location.href = options.data.url;
        }

        notification.close();
      };

      notification.onclose = () => {
        this.activeNotifications.delete(id);

        if (options.onClose) {
          options.onClose();
        }
      };

      notification.onerror = (error) => {
        console.error('Notification error:', error);

        if (options.onError) {
          options.onError(error);
        }
      };

      // Auto close after duration
      if (options.duration) {
        setTimeout(() => {
          notification.close();
        }, options.duration);
      }

      return notification;

    } catch (error) {
      console.error('Failed to create notification:', error);
      return null;
    }
  }

  async sendRichNotification(title, options) {
    const richOptions = {
      ...options,
      actions: options.actions || [
        {
          action: 'view',
          title: 'View',
          icon: '/icons/view.png'
        },
        {
          action: 'dismiss',
          title: 'Dismiss',
          icon: '/icons/dismiss.png'
        }
      ]
    };

    return this.send(title, richOptions);
  }

  schedule(title, options, delay) {
    return new Promise((resolve) => {
      const timeoutId = setTimeout(async () => {
        const notification = await this.send(title, options);
        resolve(notification);
      }, delay);

      // Store for cancellation
      this.queue.push({
        timeoutId,
        title,
        options,
        scheduledTime: Date.now() + delay
      });
    });
  }

  cancelScheduled(index) {
    if (this.queue[index]) {
      clearTimeout(this.queue[index].timeoutId);
      this.queue.splice(index, 1);
    }
  }

  closeAll() {
    this.activeNotifications.forEach(notification => {
      notification.close();
    });
    this.activeNotifications.clear();
  }

  closeByTag(tag) {
    const notification = this.activeNotifications.get(tag);
    if (notification) {
      notification.close();
      this.activeNotifications.delete(tag);
    }
  }
}

// Service Worker for Background Notifications
// sw.js
self.addEventListener('push', (event) => {
  const data = event.data.json();

  const options = {
    body: data.body,
    icon: data.icon || '/icon.png',
    badge: '/badge.png',
    data: data.data,
    actions: data.actions || [
      { action: 'open', title: 'Open' },
      { action: 'close', title: 'Close' }
    ],
    tag: data.tag,
    requireInteraction: data.requireInteraction || false
  };

  event.waitUntil(
    self.registration.showNotification(data.title, options)
  );
});

self.addEventListener('notificationclick', (event) => {
  event.notification.close();

  if (event.action === 'open') {
    event.waitUntil(
      clients.openWindow(event.notification.data.url || '/')
    );
  }
});

// Usage Examples
const notificationManager = new NotificationManager();

// 1. Simple notification
await notificationManager.send('New Message', {
  body: 'You have a new message from John',
  icon: '/avatars/john.png',
  tag: 'message-123'
});

// 2. Rich notification with actions
await notificationManager.sendRichNotification('Meeting Reminder', {
  body: 'Team standup in 5 minutes',
  data: { url: '/meetings/standup' },
  actions: [
    { action: 'join', title: 'Join Now', icon: '/icons/video.png' },
    { action: 'snooze', title: 'Snooze', icon: '/icons/clock.png' }
  ],
  onClick: (event) => {
    if (event.action === 'snooze') {
      notificationManager.schedule('Meeting Reminder', {
        body: 'Team standup starting now!'
      }, 5 * 60 * 1000); // 5 minutes
    }
  }
});

// 3. Scheduled notification
await notificationManager.schedule('Take a Break', {
  body: 'You\'ve been working for 2 hours',
  icon: '/icons/break.png',
  duration: 10000
}, 2 * 60 * 60 * 1000); // 2 hours

// 4. Progressive enhancement with fallback
async function notifyUser(message) {
  if ('Notification' in window) {
    await notificationManager.send('App Notification', {
      body: message
    });
  } else {
    // Fallback to in-app notification
    showInAppNotification(message);
  }
}
Enter fullscreen mode Exit fullscreen mode

Best Practices

  1. Request Permission Contextually: Ask when user performs relevant action
  2. Don't Spam: Respect user attention and frequency
  3. Provide Value: Only send meaningful notifications
  4. Allow Control: Let users manage preferences
  5. Handle Errors: Always check permission status

Learn More


6. Web Speech API - Voice Interaction 🎀

The Web Speech API enables speech recognition (speech-to-text) and speech synthesis (text-to-speech) capabilities.

Complete Voice Assistant Implementation

class VoiceAssistant {
  constructor(options = {}) {
    this.options = {
      lang: options.lang || 'en-US',
      continuous: options.continuous || false,
      interimResults: options.interimResults || true,
      maxAlternatives: options.maxAlternatives || 1,
      ...options
    };

    this.recognition = null;
    this.synthesis = window.speechSynthesis;
    this.isListening = false;
    this.commands = new Map();
    this.transcript = '';

    this.initRecognition();
  }

  initRecognition() {
    const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;

    if (!SpeechRecognition) {
      console.error('Speech recognition not supported');
      return;
    }

    this.recognition = new SpeechRecognition();
    Object.assign(this.recognition, this.options);

    this.recognition.onstart = () => {
      this.isListening = true;
      this.onListeningStart?.();
    };

    this.recognition.onend = () => {
      this.isListening = false;
      this.onListeningEnd?.();

      if (this.options.continuous && this.shouldRestart) {
        this.start();
      }
    };

    this.recognition.onresult = (event) => {
      const results = Array.from(event.results);
      const current = results[event.resultIndex];

      const transcript = current[0].transcript;
      const isFinal = current.isFinal;
      const confidence = current[0].confidence;

      if (isFinal) {
        this.transcript = transcript;
        this.processCommand(transcript, confidence);
      }

      this.onResult?.({ transcript, isFinal, confidence });
    };

    this.recognition.onerror = (event) => {
      console.error('Speech recognition error:', event.error);
      this.onError?.(event.error);
    };
  }

  registerCommand(pattern, callback, options = {}) {
    const id = options.id || `cmd-${this.commands.size}`;

    this.commands.set(id, {
      pattern: pattern instanceof RegExp ? pattern : new RegExp(pattern, 'i'),
      callback,
      priority: options.priority || 0,
      requiresConfidence: options.requiresConfidence || 0.7
    });

    return id;
  }

  unregisterCommand(id) {
    this.commands.delete(id);
  }

  processCommand(transcript, confidence) {
    const sortedCommands = Array.from(this.commands.values())
      .sort((a, b) => b.priority - a.priority);

    for (const command of sortedCommands) {
      if (confidence < command.requiresConfidence) continue;

      const match = transcript.match(command.pattern);
      if (match) {
        command.callback(match, transcript, confidence);
        return true;
      }
    }

    this.onUnknownCommand?.(transcript, confidence);
    return false;
  }

  speak(text, options = {}) {
    return new Promise((resolve, reject) => {
      if (!this.synthesis) {
        reject(new Error('Speech synthesis not supported'));
        return;
      }

      const utterance = new SpeechSynthesisUtterance(text);

      utterance.lang = options.lang || this.options.lang;
      utterance.voice = options.voice || this.getVoice(options.voiceName);
      utterance.volume = options.volume ?? 1;
      utterance.rate = options.rate ?? 1;
      utterance.pitch = options.pitch ?? 1;

      utterance.onend = () => resolve();
      utterance.onerror = (error) => reject(error);

      if (this.isListening) {
        this.pause();
      }

      this.synthesis.speak(utterance);

      utterance.onend = () => {
        resolve();
        if (this.options.continuous) {
          this.resume();
        }
      };
    });
  }

  getVoice(voiceName) {
    if (!voiceName) return null;

    const voices = this.synthesis.getVoices();
    return voices.find(voice => 
      voice.name.toLowerCase().includes(voiceName.toLowerCase())
    );
  }

  getVoices() {
    return this.synthesis.getVoices();
  }

  start() {
    if (!this.recognition) {
      console.error('Speech recognition not initialized');
      return;
    }

    try {
      this.shouldRestart = true;
      this.recognition.start();
    } catch (error) {
      if (error.message.includes('already started')) {
        console.warn('Recognition already started');
      } else {
        console.error('Failed to start recognition:', error);
      }
    }
  }

  stop() {
    this.shouldRestart = false;
    this.recognition?.stop();
  }

  pause() {
    this.recognition?.stop();
  }

  resume() {
    if (this.shouldRestart) {
      this.recognition?.start();
    }
  }

  abort() {
    this.recognition?.abort();
    this.synthesis.cancel();
  }
}

// Usage Example: Smart Home Controller
const assistant = new VoiceAssistant({
  lang: 'en-US',
  continuous: true,
  interimResults: true
});

// Register commands
assistant.registerCommand(
  /turn (on|off) the (.*)/i,
  (match) => {
    const [, action, device] = match;
    console.log(`${action} ${device}`);
    assistant.speak(`Turning ${action} the ${device}`);

    // API call to control device
    controlDevice(device, action);
  },
  { priority: 10 }
);

assistant.registerCommand(
  /what(?:'s| is) the (weather|temperature)/i,
  async (match) => {
    const [, query] = match;
    const data = await fetch(`/api/${query}`).then(r => r.json());
    assistant.speak(`The current ${query} is ${data.value}`);
  },
  { priority: 8 }
);

assistant.registerCommand(
  /set (timer|alarm) for (\d+) (minutes?|hours?)/i,
  (match) => {
    const [, type, duration, unit] = match;
    const ms = unit.includes('hour') 
      ? duration * 60 * 60 * 1000 
      : duration * 60 * 1000;

    setTimeout(() => {
      assistant.speak(`Your ${type} for ${duration} ${unit} is up!`);
    }, ms);

    assistant.speak(`${type} set for ${duration} ${unit}`);
  },
  { priority: 9 }
);

assistant.registerCommand(
  /play (.*)/i,
  (match) => {
    const [, song] = match;
    assistant.speak(`Playing ${song}`);
    playMusic(song);
  }
);

// Event handlers
assistant.onListeningStart = () => {
  document.querySelector('.mic-icon').classList.add('listening');
};

assistant.onListeningEnd = () => {
  document.querySelector('.mic-icon').classList.remove('listening');
};

assistant.onResult = ({ transcript, isFinal, confidence }) => {
  const display = document.querySelector('.transcript');
  display.textContent = transcript;

  if (isFinal) {
    display.classList.add('final');
  }
};

assistant.onUnknownCommand = (transcript, confidence) => {
  if (confidence > 0.6) {
    assistant.speak("I'm sorry, I didn't understand that command");
  }
};

// Voice selection UI
const voiceSelect = document.querySelector('#voice-select');
window.speechSynthesis.onvoiceschanged = () => {
  const voices = assistant.getVoices();
  voices.forEach(voice => {
    const option = document.createElement('option');
    option.value = voice.name;
    option.textContent = `${voice.name} (${voice.lang})`;
    voiceSelect.appendChild(option);
  });
};

// Start listening
assistant.start();
Enter fullscreen mode Exit fullscreen mode

Multilingual Support

// Add language switching
const multilingualAssistant = new VoiceAssistant({
  lang: 'en-US',
  continuous: true
});

const languages = {
  'en': 'en-US',
  'es': 'es-ES',
  'fr': 'fr-FR',
  'de': 'de-DE',
  'hi': 'hi-IN',
  'ja': 'ja-JP'
};

multilingualAssistant.registerCommand(
  /switch (language|lang) to (.*)/i,
  (match) => {
    const [, , langName] = match;
    const langCode = languages[langName.toLowerCase()];

    if (langCode) {
      multilingualAssistant.recognition.lang = langCode;
      multilingualAssistant.options.lang = langCode;
      multilingualAssistant.speak(`Language switched to ${langName}`, {
        lang: langCode
      });
      multilingualAssistant.stop();
      multilingualAssistant.start();
    }
  }
);
Enter fullscreen mode Exit fullscreen mode

Learn More


7. WebSocket API - Real-Time Bidirectional Communication πŸ”„

WebSocket provides full-duplex communication channels over a single TCP connection, perfect for real-time applications.

Production-Ready WebSocket Manager

class WebSocketManager {
  constructor(url, options = {}) {
    this.url = url;
    this.options = {
      reconnect: true,
      reconnectInterval: 1000,
      reconnectDecay: 1.5,
      maxReconnectInterval: 30000,
      maxReconnectAttempts: Infinity,
      heartbeatInterval: 30000,
      heartbeatMessage: JSON.stringify({ type: 'ping' }),
      ...options
    };

    this.socket = null;
    this.reconnectAttempts = 0;
    this.reconnectTimer = null;
    this.heartbeatTimer = null;
    this.listeners = new Map();
    this.messageQueue = [];
    this.isConnected = false;

    this.connect();
  }

  connect() {
    try {
      this.socket = new WebSocket(this.url);

      this.socket.onopen = () => this.handleOpen();
      this.socket.onmessage = (event) => this.handleMessage(event);
      this.socket.onerror = (error) => this.handleError(error);
      this.socket.onclose = (event) => this.handleClose(event);

    } catch (error) {
      console.error('WebSocket connection failed:', error);
      this.attemptReconnect();
    }
  }

  handleOpen() {
    console.log('WebSocket connected');
    this.isConnected = true;
    this.reconnectAttempts = 0;

    // Send queued messages
    while (this.messageQueue.length > 0) {
      const message = this.messageQueue.shift();
      this.send(message);
    }

    // Start heartbeat
    this.startHeartbeat();

    // Trigger onopen listeners
    this.emit('open');
  }

  handleMessage(event) {
    try {
      const data = JSON.parse(event.data);

      // Handle pong response
      if (data.type === 'pong') {
        return;
      }

      // Emit to specific event listeners
      if (data.type) {
        this.emit(data.type, data);
      }

      // Emit to general message listeners
      this.emit('message', data);

    } catch (error) {
      // Handle non-JSON messages
      this.emit('message', event.data);
    }
  }

  handleError(error) {
    console.error('WebSocket error:', error);
    this.emit('error', error);
  }

  handleClose(event) {
    console.log('WebSocket closed:', event.code, event.reason);
    this.isConnected = false;
    this.stopHeartbeat();
    this.emit('close', event);

    if (this.options.reconnect && !event.wasClean) {
      this.attemptReconnect();
    }
  }

  attemptReconnect() {
    if (this.reconnectAttempts >= this.options.maxReconnectAttempts) {
      console.error('Max reconnection attempts reached');
      this.emit('reconnect_failed');
      return;
    }

    this.reconnectAttempts++;

    const delay = Math.min(
      this.options.reconnectInterval * Math.pow(this.options.reconnectDecay, this.reconnectAttempts - 1),
      this.options.maxReconnectInterval
    );

    console.log(`Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);

    this.reconnectTimer = setTimeout(() => {
      this.emit('reconnecting', this.reconnectAttempts);
      this.connect();
    }, delay);
  }

  startHeartbeat() {
    this.stopHeartbeat();

    this.heartbeatTimer = setInterval(() => {
      if (this.isConnected) {
        this.send(this.options.heartbeatMessage);
      }
    }, this.options.heartbeatInterval);
  }

  stopHeartbeat() {
    if (this.heartbeatTimer) {
      clearInterval(this.heartbeatTimer);
      this.heartbeatTimer = null;
    }
  }

  send(data) {
    const message = typeof data === 'string' ? data : JSON.stringify(data);

    if (this.isConnected && this.socket.readyState === WebSocket.OPEN) {
      try {
        this.socket.send(message);
        return true;
      } catch (error) {
        console.error('Failed to send message:', error);
        this.messageQueue.push(message);
        return false;
      }
    } else {
      // Queue message for later
      this.messageQueue.push(message);
      return false;
    }
  }

  on(event, callback) {
    if (!this.listeners.has(event)) {
      this.listeners.set(event, []);
    }
    this.listeners.get(event).push(callback);
  }

  off(event, callback) {
    if (!this.listeners.has(event)) return;

    const listeners = this.listeners.get(event);
    const index = listeners.indexOf(callback);

    if (index !== -1) {
      listeners.splice(index, 1);
    }
  }

  emit(event, data) {
    if (!this.listeners.has(event)) return;

    this.listeners.get(event).forEach(callback => {
      try {
        callback(data);
      } catch (error) {
        console.error(`Error in ${event} listener:`, error);
      }
    });
  }

  close(code = 1000, reason = 'Normal closure') {
    this.options.reconnect = false;

    if (this.reconnectTimer) {
      clearTimeout(this.reconnectTimer);
    }

    this.stopHeartbeat();

    if (this.socket) {
      this.socket.close(code, reason);
    }
  }

  getState() {
    if (!this.socket) return 'CLOSED';

    const states = {
      [WebSocket.CONNECTING]: 'CONNECTING',
      [WebSocket.OPEN]: 'OPEN',
      [WebSocket.CLOSING]: 'CLOSING',
      [WebSocket.CLOSED]: 'CLOSED'
    };

    return states[this.socket.readyState];
  }
}

// Real-time Chat Application
class ChatApplication {
  constructor(serverUrl) {
    this.ws = new WebSocketManager(serverUrl, {
      reconnect: true,
      heartbeatInterval: 30000
    });

    this.currentUser = null;
    this.setupEventListeners();
  }

  setupEventListeners() {
    this.ws.on('open', () => {
      console.log('Chat connected');
      this.showStatus('Connected', 'success');
    });

    this.ws.on('close', () => {
      this.showStatus('Disconnected', 'error');
    });

    this.ws.on('reconnecting', (attempt) => {
      this.showStatus(`Reconnecting (${attempt})...`, 'warning');
    });

    this.ws.on('message:chat', (data) => {
      this.displayMessage(data);
    });

    this.ws.on('user:joined', (data) => {
      this.showNotification(`${data.username} joined the chat`);
      this.updateUserList(data.users);
    });

    this.ws.on('user:left', (data) => {
      this.showNotification(`${data.username} left the chat`);
      this.updateUserList(data.users);
    });

    this.ws.on('typing', (data) => {
      this.showTypingIndicator(data.username);
    });
  }

  login(username) {
    this.currentUser = username;
    this.ws.send({
      type: 'auth',
      username: username,
      timestamp: Date.now()
    });
  }

  sendMessage(text) {
    const message = {
      type: 'message:chat',
      user: this.currentUser,
      text: text,
      timestamp: Date.now()
    };

    this.ws.send(message);
    this.displayMessage(message, true); // Display optimistically
  }

  sendTypingIndicator() {
    this.ws.send({
      type: 'typing',
      user: this.currentUser
    });
  }

  displayMessage(data, isOwn = false) {
    const container = document.getElementById('messages');
    const messageEl = document.createElement('div');
    messageEl.className = `message ${isOwn ? 'own' : ''}`;
    messageEl.innerHTML = `
      <div class="message-user">${data.user}</div>
      <div class="message-text">${this.escapeHtml(data.text)}</div>
      <div class="message-time">${new Date(data.timestamp).toLocaleTimeString()}</div>
    `;
    container.appendChild(messageEl);
    container.scrollTop = container.scrollHeight;
  }

  showTypingIndicator(username) {
    const indicator = document.getElementById('typing-indicator');
    indicator.textContent = `${username} is typing...`;
    indicator.style.display = 'block';

    // Hide after 3 seconds
    clearTimeout(this.typingTimeout);
    this.typingTimeout = setTimeout(() => {
      indicator.style.display = 'none';
    }, 3000);
  }

  showStatus(message, type) {
    const status = document.getElementById('status');
    status.textContent = message;
    status.className = `status ${type}`;
  }

  showNotification(message) {
    // Implementation
  }

  updateUserList(users) {
    const list = document.getElementById('user-list');
    list.innerHTML = users.map(user => `
      <div class="user">${this.escapeHtml(user)}</div>
    `).join('');
  }

  escapeHtml(text) {
    const div = document.createElement('div');
    div.textContent = text;
    return div.innerHTML;
  }
}

// Usage
const chat = new ChatApplication('wss://your-server.com/chat');

document.getElementById('login-btn').addEventListener('click', () => {
  const username = document.getElementById('username').value;
  chat.login(username);
});

document.getElementById('send-btn').addEventListener('click', () => {
  const text = document.getElementById('message-input').value;
  if (text.trim()) {
    chat.sendMessage(text);
    document.getElementById('message-input').value = '';
  }
});

let typingTimer;
document.getElementById('message-input').addEventListener('input', () => {
  clearTimeout(typingTimer);
  chat.sendTypingIndicator();

  typingTimer = setTimeout(() => {
    // Stop typing indicator
  }, 1000);
});
Enter fullscreen mode Exit fullscreen mode

Server-Side Example (Node.js)

// server.js
const WebSocket = require('ws');

const wss = new WebSocket.Server({ port: 8080 });

const clients = new Map();

wss.on('connection', (ws) => {
  let username = null;

  ws.on('message', (data) => {
    try {
      const message = JSON.parse(data);

      switch (message.type) {
        case 'auth':
          username = message.username;
          clients.set(ws, username);

          // Broadcast user joined
          broadcast({
            type: 'user:joined',
            username: username,
            users: Array.from(clients.values())
          }, ws);
          break;

        case 'message:chat':
          // Broadcast to all except sender
          broadcast(message, ws);
          break;

        case 'typing':
          broadcast(message, ws);
          break;

        case 'ping':
          ws.send(JSON.stringify({ type: 'pong' }));
          break;
      }
    } catch (error) {
      console.error('Message parse error:', error);
    }
  });

  ws.on('close', () => {
    if (username) {
      clients.delete(ws);

      broadcast({
        type: 'user:left',
        username: username,
        users: Array.from(clients.values())
      });
    }
  });
});

function broadcast(message, exclude = null) {
  const data = JSON.stringify(message);

  clients.forEach((username, client) => {
    if (client !== exclude && client.readyState === WebSocket.OPEN) {
      client.send(data);
    }
  });
}

console.log('WebSocket server running on port 8080');
Enter fullscreen mode Exit fullscreen mode

Learn More


8. Service Worker API - Offline-First Web Apps πŸ“΄

Service Workers act as a proxy between web applications and the network, enabling offline functionality, background sync, and push notifications.

Complete PWA Implementation

// sw.js - Service Worker
const CACHE_VERSION = 'v1.0.0';
const CACHE_NAMES = {
  static: `static-${CACHE_VERSION}`,
  dynamic: `dynamic-${CACHE_VERSION}`,
  images: `images-${CACHE_VERSION}`
};

const STATIC_ASSETS = [
  '/',
  '/index.html',
  '/styles/main.css',
  '/scripts/app.js',
  '/offline.html',
  '/manifest.json'
];

// Install event - cache static assets
self.addEventListener('install', (event) => {
  console.log('Service Worker installing...');

  event.waitUntil(
    caches.open(CACHE_NAMES.static)
      .then(cache => cache.addAll(STATIC_ASSETS))
      .then(() => self.skipWaiting())
  );
});

// Activate event - cleanup old caches
self.addEventListener('activate', (event) => {
  console.log('Service Worker activating...');

  event.waitUntil(
    caches.keys()
      .then(cacheNames => {
        return Promise.all(
          cacheNames
            .filter(name => !Object.values(CACHE_NAMES).includes(name))
            .map(name => caches.delete(name))
        );
      })
      .then(() => self.clients.claim())
  );
});

// Fetch event - serve from cache, fallback to network
self.addEventListener('fetch', (event) => {
  const { request } = event;
  const url = new URL(request.url);

  // Handle different resource types
  if (request.destination === 'image') {
    event.respondWith(handleImageRequest(request));
  } else if (url.origin === location.origin) {
    event.respondWith(handleSameOriginRequest(request));
  } else {
    event.respondWith(handleCrossOriginRequest(request));
  }
});

async function handleImageRequest(request) {
  const cache = await caches.open(CACHE_NAMES.images);
  const cached = await cache.match(request);

  if (cached) {
    return cached;
  }

  try {
    const response = await fetch(request);

    if (response.ok) {
      cache.put(request, response.clone());
    }

    return response;
  } catch (error) {
    // Return placeholder image
    return caches.match('/images/placeholder.png');
  }
}

async function handleSameOriginRequest(request) {
  // Network-first strategy for API calls
  if (request.url.includes('/api/')) {
    return networkFirst(request);
  }

  // Cache-first strategy for static assets
  return cacheFirst(request);
}

async function handleCrossOriginRequest(request) {
  try {
    const response = await fetch(request);
    return response;
  } catch (error) {
    return new Response('Network error', { status: 503 });
  }
}

async function cacheFirst(request) {
  const cached = await caches.match(request);

  if (cached) {
    // Update cache in background
    updateCache(request);
    return cached;
  }

  try {
    const response = await fetch(request);
    const cache = await caches.open(CACHE_NAMES.dynamic);
    cache.put(request, response.clone());
    return response;
  } catch (error) {
    return caches.match('/offline.html');
  }
}

async function networkFirst(request) {
  try {
    const response = await fetch(request);
    const cache = await caches.open(CACHE_NAMES.dynamic);
    cache.put(request, response.clone());
    return response;
  } catch (error) {
    const cached = await caches.match(request);
    return cached || new Response(JSON.stringify({ error: 'Offline' }), {
      headers: { 'Content-Type': 'application/json' }
    });
  }
}

async function updateCache(request) {
  try {
    const response = await fetch(request);
    const cache = await caches.open(CACHE_NAMES.dynamic);
    cache.put(request, response);
  } catch (error) {
    // Silently fail
  }
}

// Background Sync
self.addEventListener('sync', (event) => {
  if (event.tag === 'sync-data') {
    event.waitUntil(syncData());
  }
});

async function syncData() {
  const db = await openDB();
  const pendingData = await db.getAll('pending-requests');

  for (const item of pendingData) {
    try {
      await fetch(item.url, {
        method: item.method,
        headers: item.headers,
        body: item.body
      });

      await db.delete('pending-requests', item.id);
    } catch (error) {
      console.error('Sync failed:', error);
    }
  }
}

// Push Notifications
self.addEventListener('push', (event) => {
  const data = event.data ? event.data.json() : {};

  const options = {
    body: data.body || 'New notification',
    icon: data.icon || '/icon.png',
    badge: '/badge.png',
    data: data.data || {},
    actions: data.actions || []
  };

  event.waitUntil(
    self.registration.showNotification(data.title || 'Notification', options)
  );
});

self.addEventListener('notificationclick', (event) => {
  event.notification.close();

  event.waitUntil(
    clients.matchAll({ type: 'window', includeUncontrolled: true })
      .then(clientList => {
        // Focus existing window or open new one
        for (const client of clientList) {
          if (client.url === event.notification.data.url && 'focus' in client) {
            return client.focus();
          }
        }

        if (clients.openWindow) {
          return clients.openWindow(event.notification.data.url || '/');
        }
      })
  );
});

// Message handling
self.addEventListener('message', (event) => {
  if (event.data.action === 'skipWaiting') {
    self.skipWaiting();
  }

  if (event.data.action === 'clearCache') {
    event.waitUntil(
      caches.keys().then(names => 
        Promise.all(names.map(name => caches.delete(name)))
      )
    );
  }
});

// main.js - Register Service Worker
class ServiceWorkerManager {
  constructor(swPath = '/sw.js') {
    this.swPath = swPath;
    this.registration = null;
  }

  async register() {
    if (!('serviceWorker' in navigator)) {
      console.warn('Service Workers not supported');
      return false;
    }

    try {
      this.registration = await navigator.serviceWorker.register(this.swPath);
      console.log('Service Worker registered:', this.registration);

      // Check for updates
      this.registration.addEventListener('updatefound', () => {
        this.handleUpdate(this.registration.installing);
      });

      // Handle controller change
      navigator.serviceWorker.addEventListener('controllerchange', () => {
        console.log('New Service Worker activated');
        this.showUpdateNotification();
      });

      return true;
    } catch (error) {
      console.error('Service Worker registration failed:', error);
      return false;
    }
  }

  handleUpdate(worker) {
    worker.addEventListener('statechange', () => {
      if (worker.state === 'installed' && navigator.serviceWorker.controller) {
        // New update available
        this.showUpdatePrompt(worker);
      }
    });
  }

  showUpdatePrompt(worker) {
    const shouldUpdate = confirm('New version available! Reload to update?');

    if (shouldUpdate) {
      worker.postMessage({ action: 'skipWaiting' });
    }
  }

  showUpdateNotification() {
    const notification = document.createElement('div');
    notification.className = 'update-notification';
    notification.innerHTML = `
      <p>App updated! Reloading...</p>
    `;
    document.body.appendChild(notification);

    setTimeout(() => window.location.reload(), 1000);
  }

  async unregister() {
    if (this.registration) {
      return this.registration.unregister();
    }
    return false;
  }

  async update() {
    if (this.registration) {
      return this.registration.update();
    }
  }

  async enableBackgroundSync() {
    if (!this.registration) return false;

    try {
      await this.registration.sync.register('sync-data');
      return true;
    } catch (error) {
      console.error('Background sync registration failed:', error);
      return false;
    }
  }

  async subscribeToPush(vapidPublicKey) {
    if (!this.registration) return null;

    try {
      const subscription = await this.registration.pushManager.subscribe({
        userVisibleOnly: true,
        applicationServerKey: this.urlBase64ToUint8Array(vapidPublicKey)
      });

      return subscription;
    } catch (error) {
      console.error('Push subscription failed:', error);
      return null;
    }
  }

  urlBase64ToUint8Array(base64String) {
    const padding = '='.repeat((4 - base64String.length % 4) % 4);
    const base64 = (base64String + padding)
      .replace(/\\-/g, '+')
      .replace(/_/g, '/');

    const rawData = window.atob(base64);
    const outputArray = new Uint8Array(rawData.length);

    for (let i = 0; i < rawData.length; ++i) {
      outputArray[i] = rawData.charCodeAt(i);
    }

    return outputArray;
  }
}

// Usage
const swManager = new ServiceWorkerManager();

// Register on page load
window.addEventListener('load', async () => {
  const registered = await swManager.register();

  if (registered) {
    // Enable background sync
    await swManager.enableBackgroundSync();

    // Subscribe to push notifications
    const subscription = await swManager.subscribeToPush(VAPID_PUBLIC_KEY);

    if (subscription) {
      // Send subscription to server
      await fetch('/api/subscribe', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(subscription)
      });
    }
  }
});

// Queue requests for background sync when offline
window.addEventListener('offline', () => {
  console.log('App is offline. Requests will be queued.');
  document.body.classList.add('offline');
});

window.addEventListener('online', () => {
  console.log('App is online. Syncing queued requests.');
  document.body.classList.remove('offline');
  swManager.enableBackgroundSync();
});
Enter fullscreen mode Exit fullscreen mode

manifest.json

{
  "name": "My Progressive Web App",
  "short_name": "MyPWA",
  "description": "A modern progressive web application",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#007bff",
  "orientation": "portrait-primary",
  "icons": [
    {
      "src": "/icons/icon-72x72.png",
      "sizes": "72x72",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-96x96.png",
      "sizes": "96x96",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-128x128.png",
      "sizes": "128x128",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-144x144.png",
      "sizes": "144x144",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-152x152.png",
      "sizes": "152x152",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-192x192.png",
      "sizes": "192x192",
      "type": "image/png",
      "purpose": "maskable"
    },
    {
      "src": "/icons/icon-384x384.png",
      "sizes": "384x384",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ],
  "shortcuts": [
    {
      "name": "New Post",
      "url": "/new-post",
      "icons": [{ "src": "/icons/new-post.png", "sizes": "96x96" }]
    },
    {
      "name": "Messages",
      "url": "/messages",
      "icons": [{ "src": "/icons/messages.png", "sizes": "96x96" }]
    }
  ],
  "categories": ["productivity", "social"],
  "screenshots": [
    {
      "src": "/screenshots/home.png",
      "sizes": "540x720",
      "type": "image/png"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Learn More


9. Resize Observer API - Responsive Element Monitoring πŸ“

The Resize Observer API reports changes to the dimensions of an Element's content or border box.

Implementation

class ResponsiveManager {
  constructor() {
    this.observers = new Map();
    this.breakpoints = {
      mobile: 480,
      tablet: 768,
      desktop: 1024,
      wide: 1440
    };
  }

  observe(element, callback, options = {}) {
    const observer = new ResizeObserver(entries => {
      entries.forEach(entry => {
        const { width, height } = entry.contentRect;
        const { blockSize, inlineSize } = entry.contentBoxSize[0] || {};

        const data = {
          width,
          height,
          blockSize,
          inlineSize,
          element: entry.target,
          breakpoint: this.getBreakpoint(width)
        };

        callback(data, entry);
      });
    });

    observer.observe(element, options);
    this.observers.set(element, observer);

    return () => this.unobserve(element);
  }

  unobserve(element) {
    const observer = this.observers.get(element);
    if (observer) {
      observer.unobserve(element);
      this.observers.delete(element);
    }
  }

  getBreakpoint(width) {
    if (width < this.breakpoints.mobile) return 'xs';
    if (width < this.breakpoints.tablet) return 'mobile';
    if (width < this.breakpoints.desktop) return 'tablet';
    if (width < this.breakpoints.wide) return 'desktop';
    return 'wide';
  }

  disconnect() {
    this.observers.forEach(observer => observer.disconnect());
    this.observers.clear();
  }
}

// Usage
const manager = new ResponsiveManager();

manager.observe(document.querySelector('.container'), (data) => {
  console.log(`Width: ${data.width}, Breakpoint: ${data.breakpoint}`);

  // Apply responsive behavior
  if (data.breakpoint === 'mobile') {
    data.element.classList.add('mobile-layout');
  } else {
    data.element.classList.remove('mobile-layout');
  }
});
Enter fullscreen mode Exit fullscreen mode

Learn More


10-15. Additional APIs

Due to length constraints, here are brief overviews of the remaining APIs:

10. Broadcast Channel API

Cross-tab communication for syncing state across browser tabs.

const channel = new BroadcastChannel('app-channel');
channel.postMessage({ type: 'logout' });
channel.onmessage = (e) => console.log(e.data);
Enter fullscreen mode Exit fullscreen mode

11. WebGL API

Hardware-accelerated 3D graphics in the browser.

12. Payment Request API

Streamlined payment processing with native UI.

13. Credential Management API

Simplified authentication with password and biometric support.

14. Battery Status API

Monitor device battery level and charging status.

navigator.getBattery().then(battery => {
  console.log(`Battery: ${battery.level * 100}%`);
});
Enter fullscreen mode Exit fullscreen mode

15. Screen Wake Lock API

Prevent screen from dimming or locking.

const wakeLock = await navigator.wakeLock.request('screen');
Enter fullscreen mode Exit fullscreen mode

API Comparison Matrix

API Complexity Browser Support Use Case Performance Impact
IndexedDB High βœ… Excellent Large data storage Low
Web Workers Medium βœ… Excellent Heavy computation Positive (offloads main thread)
WebRTC High βœ… Good Real-time communication High (bandwidth dependent)
Intersection Observer Low βœ… Excellent Lazy loading, scroll effects Very Low
Service Worker High βœ… Good Offline functionality Positive (caching)
WebSocket Medium βœ… Excellent Real-time updates Low
Resize Observer Low βœ… Good Responsive layouts Very Low
Web Speech Medium ⚠️ Moderate Voice interfaces Medium

Learning Roadmap

Phase 1: Foundation (Weeks 1-2)

  • Storage API (localStorage/sessionStorage)
  • Fetch API
  • DOM Manipulation
  • Event Handling

Phase 2: Intermediate (Weeks 3-6)

  • Intersection Observer API
  • Clipboard API
  • Geolocation API
  • Canvas API
  • History API

Phase 3: Advanced (Weeks 7-12)

  • IndexedDB API
  • Web Workers API
  • Service Workers API
  • WebSocket API
  • Resize Observer API

Phase 4: Expert (Weeks 13+)

  • WebRTC API
  • WebGL API
  • Web Speech API
  • Payment Request API
  • Credential Management API

Best Practices

1. Feature Detection

Always check for API support before using:

if ('serviceWorker' in navigator) {
  // Use Service Worker
} else {
  // Provide fallback
}
Enter fullscreen mode Exit fullscreen mode

2. Progressive Enhancement

Build core functionality first, then enhance:

// Basic functionality
function saveData(data) {
  localStorage.setItem('data', JSON.stringify(data));
}

// Enhanced with IndexedDB
if ('indexedDB' in window) {
  async function saveData(data) {
    await db.add('data', data);
  }
}
Enter fullscreen mode Exit fullscreen mode

3. Error Handling

Always handle errors gracefully:

try {
  await navigator.clipboard.writeText(text);
} catch (error) {
  // Fallback to document.execCommand
  document.execCommand('copy');
}
Enter fullscreen mode Exit fullscreen mode

4. Performance Optimization

  • Use Web Workers for heavy computation
  • Implement lazy loading with Intersection Observer
  • Cache resources with Service Workers
  • Throttle/debounce expensive operations

5. Security Considerations

  • Validate all user input
  • Use HTTPS for sensitive APIs (WebRTC, Geolocation, etc.)
  • Implement CSP headers
  • Sanitize data before storage

Real-World Project Ideas

1. Advanced PWA E-commerce Platform

APIs Used: Service Worker, IndexedDB, Payment Request, Push Notifications
Features: Offline shopping cart, order tracking, payment processing

2. Video Conferencing Application

APIs Used: WebRTC, Web Audio, Screen Wake Lock, WebSocket
Features: Multi-party video calls, screen sharing, real-time chat

3. Voice-Controlled Task Manager

APIs Used: Web Speech, IndexedDB, Web Notifications, Service Worker
Features: Voice commands, offline sync, smart reminders

4. Real-Time Collaborative Editor

APIs Used: WebSocket, IndexedDB, Broadcast Channel, Service Worker
Features: Multi-user editing, conflict resolution, offline editing

5. 3D Product Visualizer

APIs Used: WebGL, Web Workers, IndexedDB, Intersection Observer
Features: 360Β° product views, AR preview, lazy loading


Resources

Official Documentation

Tools & Libraries

  • Workbox: Service Worker library by Google
  • Comlink: Makes Web Workers easier
  • Socket.io: WebSocket library
  • Three.js: WebGL framework
  • Dexie.js: IndexedDB wrapper

Browser Support

Learning Platforms


Conclusion

Mastering these 15 Web APIs will transform your development capabilities. From building offline-first applications with Service Workers to creating real-time collaborative experiences with WebRTC and WebSocket, these APIs provide the foundation for modern web applications.

Remember:

  • Start with the basics and gradually progress
  • Always check browser compatibility
  • Implement progressive enhancement
  • Focus on user experience
  • Keep learning and experimenting

The web platform continues to evolve, and staying current with these APIs ensures you can build cutting-edge applications that provide exceptional user experiences.


Discussion

What APIs are you most excited to implement? Have you built anything interesting with these APIs? Share your experiences in the comments!


Tags: #webdev #javascript #api #programming #webdevelopment #pwa #webrtc #indexeddb #serviceworker #advancedweb

Author: Nishant Gaurav
Last Updated: February 2, 2026

Version: 1.0

Top comments (0)