|$ curl https://forge-ai.dev/api/markdown?path=docs/web-platform/web-apis
$cat docs/web-platform-apis.md
updated Recently·35 min read·published

Web Platform APIs

Web APIsClipboardNotificationsGeolocationIntermediate🎯Free Tools
Introduction

The Web Platform provides a rich set of APIs that give web applications access to device capabilities, system features, and user interactions that were previously only available to native apps. These APIs cover everything from clipboard and notifications to payments and geolocation.

Most of these APIs require explicit user permission, work only over HTTPS, and may not be available in all browsers. Always check for feature support before using them and provide meaningful fallbacks when features are unavailable.

Clipboard API

The Clipboard API provides programmatic access to copy, cut, and paste operations. It replaces the deprecated document.execCommand methods with a modern, async API that works with text, HTML, images, and files.

clipboard-api.js
JavaScript
1// Read text from clipboard
2async function readClipboardText() {
3 try {
4 const text = await navigator.clipboard.readText();
5 return text;
6 } catch (err) {
7 if (err.name === 'NotAllowedError') {
8 console.error('Clipboard access denied by user');
9 }
10 return null;
11 }
12}
13
14// Write text to clipboard
15async function copyToClipboard(text) {
16 try {
17 await navigator.clipboard.writeText(text);
18 showToast('Copied to clipboard');
19 } catch (err) {
20 // Fallback for older browsers
21 const textarea = document.createElement('textarea');
22 textarea.value = text;
23 textarea.style.position = 'fixed';
24 textarea.style.opacity = '0';
25 document.body.appendChild(textarea);
26 textarea.select();
27 document.execCommand('copy');
28 document.body.removeChild(textarea);
29 showToast('Copied to clipboard');
30 }
31}
32
33// Read rich content (HTML, images, files)
34async function readClipboardRich() {
35 try {
36 const items = await navigator.clipboard.read();
37 for (const item of items) {
38 // Check for images
39 if (item.types.includes('image/png')) {
40 const blob = await item.getType('image/png');
41 const img = document.createElement('img');
42 img.src = URL.createObjectURL(blob);
43 document.body.appendChild(img);
44 }
45
46 // Check for HTML
47 if (item.types.includes('text/html')) {
48 const blob = await item.getType('text/html');
49 const html = await blob.text();
50 console.log('HTML content:', html);
51 }
52
53 // Check for plain text
54 if (item.types.includes('text/plain')) {
55 const blob = await item.getType('text/plain');
56 const text = await blob.text();
57 console.log('Text content:', text);
58 }
59 }
60 } catch (err) {
61 console.error('Failed to read clipboard:', err);
62 }
63}
64
65// Write rich content to clipboard
66async function copyRichContent() {
67 const html = '<strong>Bold text</strong> and <em>italic</em>';
68 const text = 'Bold text and italic';
69
70 try {
71 await navigator.clipboard.write([
72 new ClipboardItem({
73 'text/html': new Blob([html], { type: 'text/html' }),
74 'text/plain': new Blob([text], { type: 'text/plain' }),
75 }),
76 ]);
77 console.log('Rich content copied');
78 } catch (err) {
79 console.error('Failed to copy:', err);
80 }
81}
82
83// Copy image to clipboard
84async function copyImage(imageUrl) {
85 const response = await fetch(imageUrl);
86 const blob = await response.blob();
87 await navigator.clipboard.write([
88 new ClipboardItem({
89 [blob.type]: blob,
90 }),
91 ]);
92}

warning

Clipboard API requires a user gesture (click, keypress) to work. Calling readText() or writeText() outside a user gesture will fail with NotAllowedError. Browsers show a permission prompt on first read access — users can deny it.
Notifications API

The Notifications API lets you display system notifications to the user, even when the page is not focused. Notifications can include text, images, actions, and badges. They require explicit user permission.

notifications-api.js
JavaScript
1// Request notification permission
2async function requestNotificationPermission() {
3 if (!('Notification' in window)) {
4 console.log('Notifications not supported');
5 return false;
6 }
7
8 if (Notification.permission === 'granted') return true;
9 if (Notification.permission === 'denied') return false;
10
11 const permission = await Notification.requestPermission();
12 return permission === 'granted';
13}
14
15// Show a simple notification
16function showNotification(title, options = {}) {
17 if (Notification.permission !== 'granted') return;
18
19 const notification = new Notification(title, {
20 body: options.body || '',
21 icon: options.icon || '/icons/icon-192.png',
22 badge: options.badge || '/icons/badge-72.png',
23 image: options.image || null,
24 tag: options.tag || 'default',
25 renotify: options.renotify || false,
26 silent: options.silent || false,
27 vibrate: options.vibrate || [100, 50, 100],
28 timestamp: options.timestamp || Date.now(),
29 data: options.data || {},
30 actions: options.actions || [],
31 });
32
33 notification.onclick = (event) => {
34 event.preventDefault();
35 window.focus();
36 notification.close();
37 if (options.onClick) options.onClick(event);
38 };
39
40 notification.onclose = (event) => {
41 if (options.onClose) options.onClose(event);
42 };
43
44 return notification;
45}
46
47// Usage examples
48async function initNotifications() {
49 const granted = await requestNotificationPermission();
50 if (!granted) return;
51
52 // Simple notification
53 showNotification('Hello!', { body: 'Welcome to our app' });
54
55 // Notification with action buttons
56 showNotification('New Message', {
57 body: 'Alice sent you a message',
58 tag: 'chat-alice',
59 actions: [
60 { action: 'reply', title: 'Reply' },
61 { action: 'dismiss', title: 'Dismiss' },
62 ],
63 onClick: () => openChat('alice'),
64 });
65
66 // Update an existing notification by tag
67 showNotification('Upload Progress', {
68 body: '45% complete',
69 tag: 'upload-123',
70 });
71
72 // Later, update the same notification
73 setTimeout(() => {
74 showNotification('Upload Progress', {
75 body: '100% complete!',
76 tag: 'upload-123',
77 renotify: true,
78 });
79 }, 5000);
80}
81
82// Notification event handling in service worker
83self.addEventListener('notificationclick', (event) => {
84 event.notification.close();
85 const action = event.action;
86 const data = event.notification.data;
87
88 switch (action) {
89 case 'reply':
90 openReplyWindow(data.conversationId);
91 break;
92 case 'dismiss':
93 break;
94 default:
95 event.waitUntil(
96 clients.openWindow(data.url || '/')
97 );
98 }
99});

info

Use the tag option to group notifications — notifications with the same tag replace each other. Always request permission in response to a user action, not on page load. Pre-prompt users with a custom UI explaining why they should enable notifications.
Payment Request API

The Payment Request API provides a native browser checkout flow that pre-fills payment information from the user's stored methods. It reduces checkout friction and provides a consistent payment experience across websites.

payment-request.js
JavaScript
1// Check if Payment Request is available
2const canMakePayment = async () => {
3 if (!('PaymentRequest' in window)) return false;
4 try {
5 const request = new PaymentRequest(
6 [{ supportedMethods: 'basic-card' }],
7 { total: { label: 'Test', amount: { currency: 'USD', value: '0' } } }
8 );
9 return await request.canMakePayment();
10 } catch {
11 return false;
12 }
13};
14
15// Create and show a payment request
16async function processPayment(cartItems) {
17 const supportedMethods = [
18 {
19 supportedMethods: 'basic-card',
20 data: {
21 supportedNetworks: ['visa', 'mastercard', 'amex'],
22 supportedTypes: ['credit', 'debit'],
23 },
24 },
25 // Google Pay
26 {
27 supportedMethods: 'https://google.com/pay',
28 data: {
29 environment: 'TEST',
30 apiVersion: 2,
31 apiVersionMinor: 0,
32 merchantInfo: {
33 merchantId: 'YOUR_MERCHANT_ID',
34 merchantName: 'Example Store',
35 },
36 allowedPaymentMethods: [{
37 type: 'CARD',
38 parameters: {
39 allowedAuthMethods: ['PAN_ONLY', 'CRYPTOGRAM_3DS'],
40 allowedCardNetworks: ['VISA', 'MASTERCARD'],
41 },
42 tokenizationSpecification: {
43 type: 'PAYMENT_GATEWAY',
44 parameters: { gateway: 'example', gatewayMerchantId: 'id' },
45 },
46 }],
47 },
48 },
49 ];
50
51 const paymentDetails = {
52 total: {
53 label: 'Total',
54 amount: {
55 currency: 'USD',
56 value: calculateTotal(cartItems).toFixed(2),
57 },
58 },
59 displayItems: cartItems.map((item) => ({
60 label: item.name,
61 amount: { currency: 'USD', value: item.price.toFixed(2) },
62 })),
63 shippingOptions: [
64 {
65 id: 'standard',
66 label: 'Standard Shipping',
67 amount: { currency: 'USD', value: '5.99' },
68 selected: true,
69 },
70 {
71 id: 'express',
72 label: 'Express Shipping',
73 amount: { currency: 'USD', value: '14.99' },
74 },
75 ],
76 };
77
78 try {
79 const request = new PaymentRequest(supportedMethods, paymentDetails);
80
81 // Handle shipping option changes
82 request.addEventListener('shippingoptionchange', (event) => {
83 const shippingCost = event.target.shippingOption === 'express'
84 ? '14.99' : '5.99';
85 event.updateWith({
86 total: {
87 label: 'Total',
88 amount: {
89 currency: 'USD',
90 value: (calculateTotal(cartItems) + parseFloat(shippingCost)).toFixed(2),
91 },
92 },
93 });
94 });
95
96 // Show payment UI
97 const paymentResponse = await request.show();
98
99 // Send payment data to your server for processing
100 const result = await fetch('/api/process-payment', {
101 method: 'POST',
102 headers: { 'Content-Type': 'application/json' },
103 body: JSON.stringify({
104 paymentToken: paymentResponse.details,
105 method: paymentResponse.methodName,
106 }),
107 });
108
109 if (result.ok) {
110 await paymentResponse.complete('success');
111 showOrderConfirmation();
112 } else {
113 await paymentResponse.complete('fail');
114 showPaymentError();
115 }
116 } catch (err) {
117 if (err.name === 'AbortError') {
118 console.log('User cancelled payment');
119 } else {
120 console.error('Payment error:', err);
121 }
122 }
123}

best practice

Always provide a fallback checkout form for browsers that don't support the Payment Request API. Use canMakePayment() to check support before showing the native payment button. The API works best on mobile where users have payment methods saved in their browser.
Geolocation API

The Geolocation API provides access to the user's physical location. It works via GPS, WiFi, cell towers, or IP address depending on the device and available sensors. Always request permission before accessing location data.

geolocation-api.js
JavaScript
1// Check geolocation support
2if (!('geolocation' in navigator)) {
3 console.error('Geolocation not supported');
4}
5
6// Get current position
7async function getCurrentPosition() {
8 return new Promise((resolve, reject) => {
9 navigator.geolocation.getCurrentPosition(
10 (position) => {
11 resolve({
12 lat: position.coords.latitude,
13 lng: position.coords.longitude,
14 accuracy: position.coords.accuracy, // meters
15 altitude: position.coords.altitude,
16 altitudeAccuracy: position.coords.altitudeAccuracy,
17 heading: position.coords.heading, // degrees from north
18 speed: position.coords.speed, // meters/second
19 timestamp: position.timestamp,
20 });
21 },
22 (error) => {
23 switch (error.code) {
24 case error.PERMISSION_DENIED:
25 reject(new Error('Location permission denied'));
26 break;
27 case error.POSITION_UNAVAILABLE:
28 reject(new Error('Location unavailable'));
29 break;
30 case error.TIMEOUT:
31 reject(new Error('Location request timed out'));
32 break;
33 }
34 },
35 {
36 enableHighAccuracy: true, // Use GPS if available
37 timeout: 10000, // 10 second timeout
38 maximumAge: 60000, // Accept cached position up to 1 min old
39 }
40 );
41 });
42}
43
44// Watch position changes (like a moving vehicle)
45let watchId = null;
46
47function startTracking() {
48 watchId = navigator.geolocation.watchPosition(
49 (position) => {
50 updateMap({
51 lat: position.coords.latitude,
52 lng: position.coords.longitude,
53 });
54 },
55 (error) => console.error('Tracking error:', error),
56 { enableHighAccuracy: true, maximumAge: 5000 }
57 );
58}
59
60function stopTracking() {
61 if (watchId !== null) {
62 navigator.geolocation.clearWatch(watchId);
63 watchId = null;
64 }
65}
66
67// Calculate distance between two coordinates (Haversine formula)
68function haversineDistance(lat1, lon1, lat2, lon2) {
69 const R = 6371e3; // Earth radius in meters
70 const toRad = (deg) => (deg * Math.PI) / 180;
71
72 const dLat = toRad(lat2 - lat1);
73 const dLon = toRad(lon2 - lon1);
74 const a =
75 Math.sin(dLat / 2) ** 2 +
76 Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) *
77 Math.sin(dLon / 2) ** 2;
78 const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
79
80 return R * c; // distance in meters
81}
82
83// Geofencing — trigger when entering/leaving an area
84class Geofence {
85 constructor(center, radius) {
86 this.center = center;
87 this.radius = radius;
88 this.isInside = false;
89 }
90
91 check(lat, lng) {
92 const distance = haversineDistance(
93 this.center.lat, this.center.lng, lat, lng
94 );
95 const nowInside = distance <= this.radius;
96
97 if (nowInside && !this.isInside) {
98 this.onEnter?.();
99 } else if (!nowInside && this.isInside) {
100 this.onLeave?.();
101 }
102
103 this.isInside = nowInside;
104 }
105}
106
107// Usage
108const homeGeofence = new Geofence(
109 { lat: 37.7749, lng: -122.4194 },
110 100 // 100 meter radius
111);
112
113homeGeofence.onEnter = () => console.log('Welcome home!');
114homeGeofence.onLeave = () => console.log('Leaving home area');
115
116startTracking();

warning

Geolocation requires explicit user permission and HTTPS. On iOS Safari, high accuracy mode can significantly drain battery. Use enableHighAccuracy only when precision matters — for nearby searches, standard accuracy is usually sufficient and much more battery-efficient.
Device APIs

Device APIs provide access to hardware sensors like accelerometers, gyroscopes, and orientation sensors. They enable motion-based interactions, fitness tracking, and augmented reality experiences.

device-apis.js
JavaScript
1// Device Orientation (accelerometer + gyroscope)
2function handleOrientation(event) {
3 const { alpha, beta, gamma } = event;
4 // alpha: compass direction (0-360 degrees)
5 // beta: front-to-back tilt (-180 to 180 degrees)
6 // gamma: left-to-right tilt (-90 to 90 degrees)
7
8 const compass = alpha !== null ? alpha : 'N/A';
9 const tiltForward = beta !== null ? beta.toFixed(1) : 'N/A';
10 const tiltSide = gamma !== null ? gamma.toFixed(1) : 'N/A';
11
12 console.log(`Compass: ${compass}° | Forward: ${tiltForward}° | Side: ${tiltSide}°`);
13}
14
15// Request permission on iOS 13+
16async function enableOrientation() {
17 if (typeof DeviceOrientationEvent.requestPermission === 'function') {
18 const permission = await DeviceOrientationEvent.requestPermission();
19 if (permission === 'granted') {
20 window.addEventListener('deviceorientation', handleOrientation);
21 }
22 } else {
23 window.addEventListener('deviceorientation', handleOrientation);
24 }
25}
26
27// Device Motion (acceleration)
28function handleMotion(event) {
29 const { acceleration, accelerationIncludingGravity, rotationRate } = event;
30
31 console.log('Acceleration:', {
32 x: acceleration?.x,
33 y: acceleration?.y,
34 z: acceleration?.z,
35 });
36
37 console.log('Rotation:', {
38 alpha: rotationRate?.alpha, // compass rate
39 beta: rotationRate?.beta, // front-back rate
40 gamma: rotationRate?.gamma, // left-right rate
41 });
42}
43
44window.addEventListener('devicemotion', handleMotion);
45
46// Battery Status API
47async function monitorBattery() {
48 if (!('getBattery' in navigator)) return;
49
50 const battery = await navigator.getBattery();
51
52 function updateBatteryInfo() {
53 console.log(`Battery: ${(battery.level * 100).toFixed(0)}%`);
54 console.log(`Charging: ${battery.charging}`);
55 console.log(`Time to full: ${battery.chargingTime} seconds`);
56 console.log(`Time remaining: ${battery.dischargingTime} seconds`);
57 }
58
59 battery.addEventListener('levelchange', updateBatteryInfo);
60 battery.addEventListener('chargingchange', updateBatteryInfo);
61 battery.addEventListener('chargingtimechange', updateBatteryInfo);
62 battery.addEventListener('dischargingtimechange', updateBatteryInfo);
63 updateBatteryInfo();
64}
65
66// Fullscreen API
67function toggleFullscreen() {
68 if (!document.fullscreenElement) {
69 document.documentElement.requestFullscreen().catch((err) => {
70 console.error('Fullscreen failed:', err);
71 });
72 } else {
73 document.exitFullscreen();
74 }
75}
76
77// Vibration API (mobile only)
78function vibrate(pattern) {
79 if ('vibrate' in navigator) {
80 navigator.vibrate(pattern); // ms, or array of vibrate/pause
81 }
82}
83
84vibrate(200); // Vibrate 200ms
85vibrate([100, 50, 100]); // Vibrate 100ms, pause 50ms, vibrate 100ms
86
87// Page Visibility API
88document.addEventListener('visibilitychange', () => {
89 if (document.hidden) {
90 console.log('Page hidden — pause expensive operations');
91 pauseAnimations();
92 } else {
93 console.log('Page visible — resume operations');
94 resumeAnimations();
95 }
96});

info

The Device Orientation API requires a user gesture on iOS 13+ to request permission. Always handle the case where sensors are unavailable — desktop devices typically have no accelerometer or gyroscope. Use feature detection for every device API.
Page Visibility & Focus

Knowing when a page is visible or focused helps optimize resource usage — pause animations, reduce polling, pause video, or save state when the user switches away.

visibility-api.js
JavaScript
1// Page Visibility — detect tab visibility
2class VisibilityManager {
3 constructor() {
4 this.listeners = { visible: [], hidden: [] };
5 document.addEventListener('visibilitychange', () => {
6 const state = document.hidden ? 'hidden' : 'visible';
7 this.listeners[state].forEach((cb) => cb());
8 });
9 }
10
11 onVisible(callback) {
12 this.listeners.visible.push(callback);
13 }
14
15 onHidden(callback) {
16 this.listeners.hidden.push(callback);
17 }
18}
19
20const visibility = new VisibilityManager();
21visibility.onHidden(() => {
22 pauseAutoRefresh();
23 saveDraft();
24});
25visibility.onVisible(() => {
26 resumeAutoRefresh();
27 refreshData();
28});
29
30// Focus/Blur events
31window.addEventListener('focus', () => {
32 console.log('Window focused');
33 resumeWebSocket();
34 checkForUpdates();
35});
36
37window.addEventListener('blur', () => {
38 console.log('Window blurred');
39 pauseWebSocket();
40});
41
42// Document.hasFocus() — check current focus state
43if (document.hasFocus()) {
44 startRealTimeUpdates();
45} else {
46 queueUpdatesForLater();
47}
48
49// Freezing detection (Page Lifecycle API)
50document.addEventListener('freeze', () => {
51 console.log('Page frozen — release expensive resources');
52 cancelTimers();
53 closeWebSocket();
54});
55
56document.addEventListener('resume', () => {
57 console.log('Page resumed');
58 reconnectWebSocket();
59});
60
61// Practical example: pause video when tab is hidden
62const video = document.getElementById('main-video');
63visibility.onHidden(() => {
64 if (!video.paused) video.pause();
65});
66visibility.onVisible(() => {
67 if (video.dataset.wasPlaying === 'true') video.play();
68});
69video.addEventListener('play', () => { video.dataset.wasPlaying = 'true'; });
70video.addEventListener('pause', () => { video.dataset.wasPlaying = 'false'; });

best practice

Always use the Page Visibility API to pause expensive operations when the tab is hidden — it saves battery and CPU. Browsers may throttle hidden tabs aggressively, so timers and animations may not fire correctly. Save user state when the page becomes hidden.
Share & Haptics

The Web Share API triggers the native OS share dialog, letting users share text, URLs, or files to other apps. The Vibration API provides haptic feedback on mobile devices.

share-haptics.js
JavaScript
1// Web Share API — native share dialog
2async function shareContent() {
3 if (!navigator.share) {
4 // Fallback: copy link
5 await copyToClipboard(window.location.href);
6 return;
7 }
8
9 try {
10 await navigator.share({
11 title: 'Check this out',
12 text: 'Interesting article about web development',
13 url: window.location.href,
14 });
15 console.log('Share successful');
16 } catch (err) {
17 if (err.name !== 'AbortError') {
18 console.error('Share failed:', err);
19 }
20 }
21}
22
23// Share files
24async function shareFile() {
25 const response = await fetch('/report.pdf');
26 const blob = await response.blob();
27 const file = new File([blob], 'report.pdf', { type: 'application/pdf' });
28
29 if (navigator.canShare?.({ files: [file] })) {
30 await navigator.share({
31 title: 'Monthly Report',
32 files: [file],
33 });
34 }
35}
36
37// Web Share Target API — receive shared content
38// Register in manifest.json:
39// "share_target": {
40// "action": "/share-receiver",
41// "method": "POST",
42// "enctype": "multipart/form-data",
43// "params": {
44// "files": [{ "name": "file", "accept": ["image/*", "application/pdf"] }]
45// }
46// }
47
48// Vibration patterns for feedback
49const hapticPatterns = {
50 success: [50],
51 warning: [100, 50, 100],
52 error: [200, 100, 200, 100, 200],
53 click: [10],
54 longPress: [50],
55 selection: [5, 10, 5],
56};
57
58function hapticFeedback(type) {
59 if ('vibrate' in navigator) {
60 navigator.vibrate(hapticPatterns[type] || [50]);
61 }
62}
63
64// Game-like vibration
65function damageVibration() {
66 navigator.vibrate([30, 20, 60, 20, 30]);
67}
68
69function powerUpVibration() {
70 navigator.vibrate([10, 30, 10, 30, 10, 30, 50]);
71}

best practice

The Web Share API is widely supported on mobile but limited on desktop. Always provide a fallback (copy to clipboard) for browsers without share support. Use navigator.canShare() to check if a particular file type can be shared before attempting it.
Key Takeaways
  • Clipboard API replaces execCommand — async, secure, and supports rich content
  • Always request notification permission after user gesture with a clear value proposition
  • Payment Request API reduces checkout friction — provide a fallback for unsupported browsers
  • Geolocation requires HTTPS and user permission — use enableHighAccuracy sparingly
  • Device Orientation needs permission on iOS 13+ — feature-detect for desktop
  • Page Visibility API saves resources by pausing work on hidden tabs
  • Web Share API triggers native share dialogs — provide copy-to-clipboard fallback