Create And Install Locally Hosted WebRTC PWA
Learn how to build and set up a PWA application that uses WebRTC to display video from a device camera and take photos at a defined interval for upload to a locally hosted server.
Continuous screen time is needed for the mobile web browser and camera use. On Android devices, navigate to Settings > Display > Screen Timeout and set the timeout longer and if applicable set “Keep screen on while viewing”. On some Android devices, enable “Keep screen on while charging” in the “Developer Options”. For iOS devices, navigate to Settings -> Display & Brightness > Auto-Lock and set to a longer timeout or if applicable set to “Never”. On all devices, dim the screen to save to battery usage, the screen brightness settings are found in the Accessibility portion of Settings.
Remotely debug your Android device via a wireless connection. You need to select “Enable USB Debugging” in the “Developer Options” of your Android device. The USB cable will be needed in order to run some ADB commands for WiFi connection from you workstation. On the workstation, open the Google Chrome web browser and navigate to “chrome://inspect#devices”. With both Discover USB devices and Discover network targets enabled, enable port forwarding based on the workstation port number. Port forwarding can be performed on the router instead of using ADB and the chrome view, which will allow other devices such as the iPhone to be used.
Next, you need to navigate to the Android device Google Chrome web browser and enter the URL and port number. For this tutorial, the port number is original set by the PHP built-in web server. PHP also receives the images at a specified interval. PHP can be replaced with another HTTP web server and framework for handling image uploads. JavaScript is used for the client side of the application
An SSL Certificate will not be needed since the URL will contain localhost and a port number.
Requirements For Locally Hosted PWA
Glossary:
IP
Internet Protocol address is a numerical label that is assigned to a device connected to a computer network.
Wi-Fi
Wireless network protocols based on the IEEE 802.11 standards for local area networks.
WLAN
Wireless LAN is a computer network that links 2 or more devices.
TCP/IP
Transmission Control Protocol/Internet Protocol is a framework for computer networking.
USB
Universal Serial Bus is a standard for digital data transmission and power delivery between electronics.
ADB
Android Debug Bridge.
PWA
Progressive Web App is built using web platforms to provide platform-specific experiences.
Android Devices
Name | Description | Recording | Power |
---|---|---|---|
Sony Xperia XA1 Ultra | Updated to Android 8.0 and latest web browser. | Takes photos and videos on front and back cameras. | USB Type-C 2.0 10W charging |
Samsung Galaxy S21 FE 5G | Updated to Android 14.0 and default camera application. | Takes photos and videos on front and back cameras. | USB Type-C 2.0 <25W charging |
Name | Description | Example |
HTML Take Photo Every 3 Seconds (Includes iOS Fix)
<!DOCTYPE html> <html> <head> <title>Chicken Security Cam</title> <meta name="viewport" content="width=device-width, initial-scale=1" /> <style> button { color: blue; background: red; } video { width:100%; height: auto; max-height: 100%; } </style> <link rel="manifest" href="/chicken-cam-pwa-manifest.json" /> </head> <body> <select></select> <button type="button">Record</button> <video muted playsinline></video> <script src="/chicken-cam-pwa.js"></script> </body> </html>
JavaScript Take Photo Every 3 Seconds And Register PWA Service Worker
(() => { let str = null; let iid = null; let ppt = null; let vid = document.querySelector('video'); let btn = document.querySelector('button'); let sel = document.querySelector('select'); let cts = { video: { width: {min: 1920, ideal: 3840, max: 7680}, height: {min: 1080, ideal: 2160, max: 4320} }, audio: false }; navigator.mediaDevices.getUserMedia(cts).then(() => { navigator.mediaDevices.enumerateDevices().then((devices) => { let vds = devices.filter(device => device.kind == 'videoinput'); let opt = vds.map(vd => { return `<option value="${vd.deviceId}">${vd.label}</option>`; }); sel.innerHTML = `<option value="">Select Camera</option>` + opt.join(''); }); }); if ("serviceWorker" in navigator) { window.addEventListener("load", () => { // Register Server Worker navigator.serviceWorker .register("./chicken-cam-pwa-service-worker.js") .then(res => console.log("Service Worker Registered")) .catch(err => console.log("Service Worker Not Registered", err)); }); } sel.addEventListener('change', (evt) => { if (str !== null) { str.getTracks().forEach(track => { track.stop(); vid.srcObject.removetrack(track); }); vid.pause(); vid.src = ''; str = null; } if (sel.value !== "") { cts.video.deviceId = sel.value; navigator.mediaDevices.getUserMedia(cts).then((stream) => { str = stream; vid.srcObject = stream; vid.play(); }); } }); btn.addEventListener('click', (evt) => { if (btn.innerHTML === 'Record') { btn.innerHTML = 'Recording'; iid = setInterval(() => { let canvas = document.createElement('canvas'); let context = canvas.getContext('2d'); canvas.width = vid.videoWidth; canvas.height = vid.videoHeight; context.drawImage(vid, 0, 0, canvas.width, canvas.height); canvas.toBlob((blob) => { let file = new File([blob], 'image.png', {type: blob.type}); let fd = new FormData(); fd.append('image', file); fetch('save-hi-res.php', { method: 'POST', body: fd }) .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error(error)); }) }, 3000); } else { btn.innerHTML = 'Record'; clearInterval(iid); } }); })();
JavaScript Service Worker Install
self.addEventListener('install', (event) => { event.waitUntil( caches.open('chicken-cam-pwa').then((cache) => { return cache.addAll([ '/', '/chicken-cam-pwa.html', '/chicken-cam-pwa.js', '/hi-res.html' ]); }) ); }); self.addEventListener("fetch", fetchEvent => { fetchEvent.respondWith( caches.match(fetchEvent.request).then(res => { return res || fetch(fetchEvent.request); }) ); });
JSON Manifest
{ "name": "Chicken Security Camera", "short_name": "Chicken Cam", "start_url": "chicken-cam-pwa.html", "display": "standalone", "icons": [ { "src": "chicken-cam-pwa.png", "type": "image/png", "sizes": "512x512" } ], "screenshots": [ { "src": "chicken-cam-pwa-screenshot.avif", "sizes": "1080x1920", "form_factor": "narrow", "label": "Desktop view showing chicken security camera" }, { "src": "chicken-cam-pwa-screenshot-wide.avif", "sizes": "1920x1080", "form_factor": "wide", "label": "Desktop view showing chicken security camera" } ] }
JavaScript Refresh Photo Every 3 Seconds
// Reload Every 3 Seconds setInterval(function(){ let img = document.querySelector('img'); if ( !img.src.includes('?') ) { img.src = `${img.src}?${Date.now()}`; } else { img.src = img.src.slice(0, img.src.indexOf('?') + 1) + Date.now(); } }, 3000);
PHP Upload Photo
if ( !empty($_FILES['image']) ) { $file = $_FILES['image']['tmp_name']; $destination = dirname(__FILE__) . '/image.png'; if (move_uploaded_file($file, $destination) ) { echo json_encode(['status' => 'OK']); } } die();
Download
The PHP scripting language can be downloaded from Downloads & Installation Instructions and installed on your workstation.
The ADB tool can be downloaded as part of the SDK Platform Tools for Download SDK Platform Tools and decompressed into a folder on your workstation.
PHP HTTP Server
php -S localhost:8000
ADB Wireless Connection
# Command Line # adb tcpip 5555 adb tcpip connect [Android Device IP Address] adb devices






Usage
You can use any IDE or text editor and the web browser to compile and execute JavaScript code. For this tutorial, the OjamboShop.com Learning JavaScript Course Web IDE can be used to input and compile JavaScript code for the HTML5 Security Camera.
Open Source
JavaScript follows the ECMAScript standard and is licensed under the W3C Software License by web browser vendors and runtime environment vendors.
PHP scripting language is licensed under the PHP License by The PHP Group. The Zend Engine is a compiler and runtime environment for PHP and is licensed under the Zend Engine License.
Live Stream
Every day, you can join a live stream and ask questions. Check Ojambo.com for details and instructions.
Learn Programming Courses:
Get the Learning JavaScript Course or the Learning PHP Course for your web browser on any device.


Learn Programming Books:
Learning JavaScript Book is available as Learning JavaScript Paperback or Learning JavaScript Ebook.
Learning PHP Book is available as Learning JavaScript Ebook.


Conclusion:
Port forward the locally hosted port to your Android device using ADB for a wireless connection. The PWA application can be debugged or installed on the Android device without having an SSL Certificate due to the localhost URL. For iOS devices, the video tag must have muted and playsinline attrbiutes for the preview.
If you enjoy this article, consider supporting me by purchasing one of my OjamboShop.com Online Programming Courses or publications at Edward Ojambo Programming Books or simply donate here Ojambo.com Donate
References:
- Downloads & Installation Instructions For PHP
- SDK Platform Tools For Android
- Customer Sets Price Plugin for WooCommerce on Ojambo.com
- Learning JavaScript Course on OjamboShop.com
- Learning Python Course on OjamboShop.com
- Learning PHP Course on OjamboShop.com
- Learning JavaScript Paperback on Amazon
- Learning JavaScript Ebook on Amazon
- Learning Python Ebook on Amazon
- Learning PHP Ebook on Amazon
- OjamboServices.com For Custom Websites, Applications & Tutorials