diff --git a/TODO.md b/TODO.md
index 7806421..a1d9baf 100644
--- a/TODO.md
+++ b/TODO.md
@@ -14,3 +14,4 @@
8. Turn Dynamic Iterator functions into traits
9. Better handling of roots and extrema finding
10. Add closing animation for function entry
+11. Create actual icon(s) for PWA/favicon (using placeholder from eframe_template)
diff --git a/build.sh b/build.sh
index 8002abe..1ea13b3 100755
--- a/build.sh
+++ b/build.sh
@@ -33,7 +33,7 @@ sed -i 's/fatal: true/fatal: false/g' tmp/ytbn_graphing_software.js
sed -i "s/TextEncoder('utf-8')/TextEncoder('utf-8', { ignoreBOM: true, fatal: false })/g" tmp/ytbn_graphing_software.js
-cp www/index.html www/style.css tmp/
+cp www/* tmp/
echo "Total size: $(du -sb tmp)"
echo "Binary size: $(du -sb tmp/ytbn_graphing_software_bg.wasm)"
diff --git a/www/favicon.ico b/www/favicon.ico
new file mode 100644
index 0000000..61ad031
Binary files /dev/null and b/www/favicon.ico differ
diff --git a/www/icon-1024.png b/www/icon-1024.png
new file mode 100644
index 0000000..1b5868a
Binary files /dev/null and b/www/icon-1024.png differ
diff --git a/www/icon-256.png b/www/icon-256.png
new file mode 100644
index 0000000..ae72287
Binary files /dev/null and b/www/icon-256.png differ
diff --git a/www/icon_ios_touch_192.png b/www/icon_ios_touch_192.png
new file mode 100644
index 0000000..8472802
Binary files /dev/null and b/www/icon_ios_touch_192.png differ
diff --git a/www/index.html b/www/index.html
index 65647fb..980603a 100644
--- a/www/index.html
+++ b/www/index.html
@@ -1,31 +1,131 @@
-
-
-
-
- (Yet-to-be-named) Graphing Software
-
-
-
-
+
-
-
+
+
+ (Yet-to-be-named) Graphing Software
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/www/manifest.json b/www/manifest.json
new file mode 100644
index 0000000..df975a2
--- /dev/null
+++ b/www/manifest.json
@@ -0,0 +1,28 @@
+{
+ "name": "YTBN Graphing Software PWA",
+ "short_name": "ytbn-graphing-software-pwa",
+ "icons": [
+ {
+ "src": "./icon-256.png",
+ "sizes": "256x256",
+ "type": "image/png"
+ },
+ {
+ "src": "./maskable_icon_x512.png",
+ "sizes": "512x512",
+ "type": "image/png",
+ "purpose": "any maskable"
+ },
+ {
+ "src": "./icon-1024.png",
+ "sizes": "1024x1024",
+ "type": "image/png"
+ }
+ ],
+ "lang": "en-US",
+ "id": "/index.html",
+ "start_url": "./index.html",
+ "display": "standalone",
+ "background_color": "black",
+ "theme_color": "#404040"
+}
diff --git a/www/maskable_icon_x512.png b/www/maskable_icon_x512.png
new file mode 100644
index 0000000..db8df3e
Binary files /dev/null and b/www/maskable_icon_x512.png differ
diff --git a/www/style.css b/www/style.css
deleted file mode 100644
index 1159fdb..0000000
--- a/www/style.css
+++ /dev/null
@@ -1,81 +0,0 @@
-html {
- /* Remove touch delay: */
- touch-action: manipulation;
-}
-
-body {
- /* Light mode background color for what is not covered by the egui canvas,
- or where the egui canvas is translucent. */
- background: #909090;
-}
-
-@media (prefers-color-scheme: dark) {
- body {
- /* Dark mode background color for what is not covered by the egui canvas,
- or where the egui canvas is translucent. */
- background: #404040;
- }
-}
-
-/* Allow canvas to fill entire web page: */
-html,
-body {
- overflow: hidden;
- margin: 0 !important;
- padding: 0 !important;
-}
-
-/* Position canvas in center-top: */
-canvas {
- margin-right: auto;
- margin-left: auto;
- display: block;
- position: absolute;
- top: 0%;
- left: 50%;
- transform: translate(-50%, 0%);
-}
-
-.centered {
- margin-right: auto;
- margin-left: auto;
- display: block;
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- color: #f0f0f0;
- font-size: 24px;
- font-family: Ubuntu-Light, Helvetica, sans-serif;
- text-align: center;
-}
-
-/* ---------------------------------------------- */
-/* Loading animation from https://loading.io/css/ */
-.lds-dual-ring {
- display: inline-block;
- width: 24px;
- height: 24px;
-}
-
-.lds-dual-ring:after {
- content: " ";
- display: block;
- width: 24px;
- height: 24px;
- margin: 0px;
- border-radius: 50%;
- border: 3px solid #fff;
- border-color: #fff transparent #fff transparent;
- animation: lds-dual-ring 1.2s linear infinite;
-}
-
-@keyframes lds-dual-ring {
- 0% {
- transform: rotate(0deg);
- }
-
- 100% {
- transform: rotate(360deg);
- }
-}
diff --git a/www/sw.js b/www/sw.js
new file mode 100644
index 0000000..c341e1d
--- /dev/null
+++ b/www/sw.js
@@ -0,0 +1,25 @@
+var cacheName = 'ytbn-graphing-software-pwa';
+var filesToCache = [
+ './',
+ './index.html',
+ './ytbn_graphing_software.js',
+ './ytbn_graphing_software_bg.wasm',
+];
+
+/* Start the service worker and cache all of the app's content */
+self.addEventListener('install', function (e) {
+ e.waitUntil(
+ caches.open(cacheName).then(function (cache) {
+ return cache.addAll(filesToCache);
+ })
+ );
+});
+
+/* Serve cached content when offline */
+self.addEventListener('fetch', function (e) {
+ e.respondWith(
+ caches.match(e.request).then(function (response) {
+ return response || fetch(e.request);
+ })
+ );
+});