PWA Future and Emerging APIs
Progressive Web Apps are continuously evolving with new capabilities that bring them closer to native app experiences. In this final lesson, we'll explore cutting-edge web APIs and the future direction of PWAs.
Project Fugu
Project Fugu is an initiative by Google, Microsoft, Intel, and other partners to bring powerful new capabilities to the web platform. The goal is to enable web apps to do anything native apps can do.
What is Project Fugu? Named after the Japanese word for pufferfish, Project Fugu aims to "expand the web's capabilities" while maintaining security, privacy, and user control. Each new API goes through rigorous security review and origin trials before becoming available.
Core Principles
- User-centric: Features should serve real user needs
- Secure by default: All APIs require user permission and HTTPS
- Progressive: Features degrade gracefully on unsupported platforms
- Open standards: All features go through W3C standardization
File System Access API
The File System Access API allows web apps to read and write files on the user's local file system with their permission.
// Open a file picker and read file
async function openFile() {
try {
// Show file picker
const [fileHandle] = await window.showOpenFilePicker({
types: [
{
description: 'Text Files',
accept: {
'text/plain': ['.txt'],
'text/markdown': ['.md']
}
}
]
});
// Get file contents
const file = await fileHandle.getFile();
const contents = await file.text();
console.log('File contents:', contents);
return { fileHandle, contents };
} catch (error) {
console.error('Error opening file:', error);
}
}
// Save file
async function saveFile(fileHandle, contents) {
try {
// Create a writable stream
const writable = await fileHandle.createWritable();
// Write contents
await writable.write(contents);
// Close the file
await writable.close();
console.log('File saved successfully');
} catch (error) {
console.error('Error saving file:', error);
}
}
// Save new file (with picker)
async function saveFileAs(contents) {
try {
const fileHandle = await window.showSaveFilePicker({
types: [
{
description: 'Text Files',
accept: { 'text/plain': ['.txt'] }
}
]
});
await saveFile(fileHandle, contents);
return fileHandle;
} catch (error) {
console.error('Error saving file:', error);
}
}
// Example: Simple text editor
class TextEditor {
constructor() {
this.fileHandle = null;
this.isDirty = false;
}
async open() {
const result = await openFile();
if (result) {
this.fileHandle = result.fileHandle;
this.isDirty = false;
return result.contents;
}
}
async save(contents) {
if (this.fileHandle) {
await saveFile(this.fileHandle, contents);
this.isDirty = false;
} else {
this.fileHandle = await saveFileAs(contents);
}
}
async saveAs(contents) {
this.fileHandle = await saveFileAs(contents);
}
}
Use Cases: Text editors, image editors, video editors, code editors, document processors, note-taking apps, project file managers.
Web Bluetooth API
The Web Bluetooth API enables communication with Bluetooth Low Energy (BLE) devices directly from web apps.
// Connect to Bluetooth device
async function connectToBluetoothDevice() {
try {
// Request device
const device = await navigator.bluetooth.requestDevice({
filters: [
{ services: ['heart_rate'] }
],
optionalServices: ['battery_service']
});
console.log('Device:', device.name);
// Connect to GATT server
const server = await device.gatt.connect();
// Get service
const service = await server.getPrimaryService('heart_rate');
// Get characteristic
const characteristic = await service.getCharacteristic('heart_rate_measurement');
// Listen for notifications
characteristic.addEventListener('characteristicvaluechanged', event => {
const value = event.target.value;
const heartRate = value.getUint8(1);
console.log('Heart Rate:', heartRate);
});
await characteristic.startNotifications();
return { device, server, characteristic };
} catch (error) {
console.error('Bluetooth error:', error);
}
}
// Disconnect
function disconnectDevice(server) {
if (server && server.connected) {
server.disconnect();
console.log('Disconnected');
}
}
// Example: Fitness tracker integration
class FitnessTracker {
constructor() {
this.device = null;
this.heartRateData = [];
}
async connect() {
const result = await connectToBluetoothDevice();
if (result) {
this.device = result.device;
this.setupHeartRateMonitor(result.characteristic);
}
}
setupHeartRateMonitor(characteristic) {
characteristic.addEventListener('characteristicvaluechanged', event => {
const heartRate = event.target.value.getUint8(1);
this.heartRateData.push({
timestamp: Date.now(),
bpm: heartRate
});
this.onHeartRateUpdate(heartRate);
});
}
onHeartRateUpdate(bpm) {
// Update UI
console.log(`Current heart rate: ${bpm} BPM`);
}
getAverageHeartRate() {
if (this.heartRateData.length === 0) return 0;
const sum = this.heartRateData.reduce((acc, data) => acc + data.bpm, 0);
return Math.round(sum / this.heartRateData.length);
}
}
Use Cases: Fitness apps, health monitors, IoT device control, smart home apps, medical devices, gaming peripherals.
Web NFC API
The Web NFC API allows reading and writing to NFC tags from web apps on Android devices.
// Check NFC support
if ('NDEFReader' in window) {
console.log('Web NFC is supported');
}
// Read NFC tags
async function readNFCTag() {
try {
const ndef = new NDEFReader();
await ndef.scan();
console.log('NFC scan started');
ndef.addEventListener('reading', ({ message, serialNumber }) => {
console.log(`NFC tag detected: ${serialNumber}`);
for (const record of message.records) {
console.log('Record type:', record.recordType);
console.log('MIME type:', record.mediaType);
if (record.recordType === 'text') {
const textDecoder = new TextDecoder(record.encoding);
const text = textDecoder.decode(record.data);
console.log('Text:', text);
}
}
});
ndef.addEventListener('readingerror', () => {
console.error('NFC read error');
});
} catch (error) {
console.error('NFC error:', error);
}
}
// Write to NFC tag
async function writeNFCTag(text) {
try {
const ndef = new NDEFReader();
await ndef.write({
records: [
{
recordType: 'text',
data: text
},
{
recordType: 'url',
data: 'https://example.com'
}
]
});
console.log('NFC tag written successfully');
} catch (error) {
console.error('Write failed:', error);
}
}
// Example: Digital business card
class DigitalBusinessCard {
async shareViaNFC() {
const cardData = {
name: 'John Doe',
title: 'Senior Developer',
email: 'john@example.com',
phone: '+1-555-0123',
website: 'https://johndoe.dev'
};
const vCard = this.generateVCard(cardData);
try {
const ndef = new NDEFReader();
await ndef.write({
records: [{
recordType: 'mime',
mediaType: 'text/vcard',
data: new TextEncoder().encode(vCard)
}]
});
console.log('Business card shared via NFC');
} catch (error) {
console.error('NFC share failed:', error);
}
}
generateVCard(data) {
return `BEGIN:VCARD
VERSION:3.0
FN:${data.name}
TITLE:${data.title}
EMAIL:${data.email}
TEL:${data.phone}
URL:${data.website}
END:VCARD`;
}
}
Idle Detection API
The Idle Detection API allows detecting when the user is idle (not interacting with the device).
// Check idle state
async function setupIdleDetection() {
try {
// Request permission
const permission = await IdleDetector.requestPermission();
if (permission !== 'granted') {
console.log('Idle detection permission denied');
return;
}
const idleDetector = new IdleDetector();
idleDetector.addEventListener('change', () => {
const userState = idleDetector.userState; // 'active' or 'idle'
const screenState = idleDetector.screenState; // 'locked' or 'unlocked'
console.log(`User: ${userState}, Screen: ${screenState}`);
if (userState === 'idle') {
handleUserIdle();
} else {
handleUserActive();
}
});
// Start detection (check after 60 seconds of inactivity)
await idleDetector.start({
threshold: 60000 // milliseconds
});
console.log('Idle detection started');
} catch (error) {
console.error('Idle detection error:', error);
}
}
function handleUserIdle() {
console.log('User is idle');
// Pause animations, videos, reduce network activity
}
function handleUserActive() {
console.log('User is active');
// Resume normal operation
}
Use Cases: Chat applications (show as away), video conferencing (pause video when idle), games (auto-pause), dashboards (reduce updates), power-saving features.
Screen Capture API
The Screen Capture API enables capturing screen contents for recording or sharing.
// Capture screen
async function captureScreen() {
try {
const stream = await navigator.mediaDevices.getDisplayMedia({
video: {
cursor: 'always' // Include cursor in capture
},
audio: false
});
// Use the stream
const videoElement = document.querySelector('video');
videoElement.srcObject = stream;
// Record the stream
const mediaRecorder = new MediaRecorder(stream);
const chunks = [];
mediaRecorder.ondataavailable = event => {
if (event.data.size > 0) {
chunks.push(event.data);
}
};
mediaRecorder.onstop = () => {
const blob = new Blob(chunks, { type: 'video/webm' });
const url = URL.createObjectURL(blob);
// Download or save the recording
const a = document.createElement('a');
a.href = url;
a.download = 'screen-recording.webm';
a.click();
};
mediaRecorder.start();
// Stop recording after user interaction or timeout
stream.getVideoTracks()[0].addEventListener('ended', () => {
mediaRecorder.stop();
});
return { stream, mediaRecorder };
} catch (error) {
console.error('Screen capture error:', error);
}
}
Badging API
The Badging API allows setting a badge on the app icon to display notifications count.
// Set badge
async function setBadge(count) {
if ('setAppBadge' in navigator) {
try {
await navigator.setAppBadge(count);
console.log(`Badge set to ${count}`);
} catch (error) {
console.error('Badge error:', error);
}
}
}
// Clear badge
async function clearBadge() {
if ('clearAppBadge' in navigator) {
await navigator.clearAppBadge();
console.log('Badge cleared');
}
}
// Example: Notification manager
class NotificationManager {
constructor() {
this.unreadCount = 0;
}
async addNotification(notification) {
this.unreadCount++;
await this.updateBadge();
}
async markAsRead(notificationId) {
this.unreadCount = Math.max(0, this.unreadCount - 1);
await this.updateBadge();
}
async markAllAsRead() {
this.unreadCount = 0;
await clearBadge();
}
async updateBadge() {
if (this.unreadCount > 0) {
await setBadge(this.unreadCount);
} else {
await clearBadge();
}
}
}
Future PWA Capabilities
Upcoming APIs and features:
- Local Font Access: Access locally installed fonts for design tools
- EyeDropper API: Pick colors from anywhere on the screen
- Web Serial API: Communicate with serial devices (Arduino, etc.)
- Web HID API: Interact with Human Interface Devices
- Web USB API: Communicate with USB devices
- Keyboard Lock API: Capture all keyboard input (for games)
- Virtual Keyboard API: Better control over on-screen keyboards
- Window Controls Overlay: Customize the title bar area
- Multi-Screen Window Placement: Control window placement across multiple screens
Best Practices for Emerging APIs
// Feature detection pattern
function supportsFeature(feature) {
const features = {
fileSystem: 'showOpenFilePicker' in window,
bluetooth: 'bluetooth' in navigator,
nfc: 'NDEFReader' in window,
idle: 'IdleDetector' in window,
badge: 'setAppBadge' in navigator,
screenCapture: 'getDisplayMedia' in navigator.mediaDevices
};
return features[feature] || false;
}
// Progressive enhancement
async function enhanceWithNewFeatures() {
const features = {};
if (supportsFeature('fileSystem')) {
features.fileSystem = await import('./file-system-handler.js');
}
if (supportsFeature('bluetooth')) {
features.bluetooth = await import('./bluetooth-handler.js');
}
// Provide fallbacks for unsupported features
if (!features.fileSystem) {
features.fileSystem = await import('./file-system-fallback.js');
}
return features;
}
PWA Future Trends
What to expect in the next 2-3 years:
- Better iOS support: Apple is gradually adding more PWA features to Safari
- Desktop PWA maturity: Better OS integration on Windows, macOS, and Linux
- AI integration: Web APIs for machine learning and AI features
- AR/VR support: WebXR for immersive experiences
- Performance improvements: Faster JavaScript engines and better caching
- Privacy-first features: New APIs that prioritize user privacy
- Cross-device experiences: Better syncing and handoff between devices
Course Summary
Congratulations on completing the Progressive Web Apps tutorial! Let's recap what we've learned:
✓ PWA Fundamentals
- Understanding PWA principles and benefits
- Difference between PWAs and native apps
- Browser support and compatibility
✓ Core Technologies
- Service Workers for offline functionality
- Web App Manifest for installability
- HTTPS and security requirements
✓ Caching Strategies
- Cache-first, network-first, stale-while-revalidate
- Dynamic caching and cache management
- Offline fallback pages
✓ Advanced Features
- Push Notifications
- Background Sync
- IndexedDB for local storage
- Install prompts and app lifecycle
✓ Performance Optimization
- App shell architecture
- Lazy loading and code splitting
- Resource optimization
- Lighthouse audits
✓ Real-World Implementation
- Built a complete TaskMaster PWA
- Learned from successful case studies
- Deployment and monitoring strategies
✓ Future Technologies
- Project Fugu and emerging APIs
- File System, Bluetooth, NFC
- Upcoming PWA capabilities
Final Challenge: Build your own PWA from scratch that includes:
- Offline functionality with service worker
- Web app manifest and install capability
- At least one advanced API (notifications, background sync, etc.)
- Lighthouse score of 90+ in all categories
- Deploy to production with HTTPS
- Document your architecture and design decisions
Share your PWA with the community and get feedback!
Next Steps:
- Stay updated with web.dev/pwa
- Follow Project Fugu progress at fugu-tracker.web.app
- Join PWA communities and forums
- Experiment with emerging APIs in Chrome Canary
- Contribute to open-source PWA projects
- Share your knowledge and help others learn
Thank you for completing this course. Keep building amazing Progressive Web Apps!