diff --git a/README.md b/README.md index a55e624..ec6f5d7 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,14 @@ -# Just a bunch of blobs +# Fleeting Garden -[![Deploy to GitHub Pages](https://github.com/schmelczer/webgpu/actions/workflows/deploy.yml/badge.svg)](https://github.com/schmelczer/webgpu/actions/workflows/deploy.yml) +Fleeting Garden is a single-player WebGPU drawing garden. Pick a vibe palette, +draw persistent coloured paths, spawn agents from those strokes, erase locally, +and export the scene as an internal render buffer snapshot. -## todo +Check out the [agent logic](./src/pipelines/agents/agent.wgsl). -- add info page description -- add share link -- settings page - add reset link -- shareable settings -- graceful error messages when no support -- fix up generation id automatically +## Testing -Check out the [agent's logic](./src/pipelines/agents/agent.wgsl). +- `npm test` runs the Vitest unit suite. +- `npm run test:e2e` runs the Playwright Chromium smoke test. The Playwright + config builds the production bundle before serving it. +- `npx playwright install chromium` installs the local browser binary when needed. diff --git a/assets/icons/brush.svg b/assets/icons/brush.svg deleted file mode 100644 index c3a203e..0000000 --- a/assets/icons/brush.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/assets/icons/download.svg b/assets/icons/download.svg new file mode 100644 index 0000000..eb27c41 --- /dev/null +++ b/assets/icons/download.svg @@ -0,0 +1,10 @@ + + + diff --git a/assets/icons/info.svg b/assets/icons/info.svg index a047bf5..19e44e5 100644 --- a/assets/icons/info.svg +++ b/assets/icons/info.svg @@ -1,4 +1,4 @@ - + diff --git a/assets/icons/maximize.svg b/assets/icons/maximize.svg index 9c8558e..bf1f311 100644 --- a/assets/icons/maximize.svg +++ b/assets/icons/maximize.svg @@ -1,7 +1,7 @@ - + - \ No newline at end of file + diff --git a/assets/icons/minimize.svg b/assets/icons/minimize.svg index 05d54a8..19e8e7f 100644 --- a/assets/icons/minimize.svg +++ b/assets/icons/minimize.svg @@ -1,7 +1,7 @@ - + - \ No newline at end of file + diff --git a/assets/icons/restart.svg b/assets/icons/restart.svg index f87e22b..d3dce2c 100644 --- a/assets/icons/restart.svg +++ b/assets/icons/restart.svg @@ -1,4 +1,4 @@ - + diff --git a/assets/icons/settings.svg b/assets/icons/settings.svg index 176c63c..9d5e6fd 100644 --- a/assets/icons/settings.svg +++ b/assets/icons/settings.svg @@ -1,4 +1,4 @@ - + diff --git a/assets/icons/sound.svg b/assets/icons/sound.svg new file mode 100644 index 0000000..4f382b5 --- /dev/null +++ b/assets/icons/sound.svg @@ -0,0 +1,3 @@ + + + diff --git a/index.html b/index.html index 17b5847..fadc28f 100644 --- a/index.html +++ b/index.html @@ -6,76 +6,243 @@ name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover" /> - + + + - + + + + + + - - - - + + + + + + - - - - + + + + + - Just a bunch of blobs + + + + + + + + + + Fleeting Garden - +
- + + Your browser cannot display the interactive WebGPU garden canvas. Use a browser + with WebGPU support to draw coloured paths and watch the garden grow. + +

+ Fleeting Garden is a pointer-driven WebGPU drawing canvas. Drag or touch the scene + to paint coloured paths, then use the toolbar to change colours, erase, export, + adjust the config overlay, restart, or open more information. +

+ + +
+ +
+
+

Fleeting Garden

+

+ Tend it while you can. The garden returns to weather either way. +

+ +
+ +
- diff --git a/public/404.html b/public/404.html deleted file mode 100644 index 7c62e5c..0000000 --- a/public/404.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - Not found - - - - - -
-

Page not found.

- Go back -
- - diff --git a/public/apple-touch-icon-180x180.png b/public/apple-touch-icon-180x180.png index b8bfae3..257c79a 100644 Binary files a/public/apple-touch-icon-180x180.png and b/public/apple-touch-icon-180x180.png differ diff --git a/public/favicon.ico b/public/favicon.ico index e545180..d26cebe 100644 Binary files a/public/favicon.ico and b/public/favicon.ico differ diff --git a/public/favicon.svg b/public/favicon.svg index 50bb5e6..c0ad1e9 100644 --- a/public/favicon.svg +++ b/public/favicon.svg @@ -1,6 +1,31 @@ - - - - + + + + + + + + + + + + + + + + + diff --git a/public/manifest.webmanifest b/public/manifest.webmanifest index f7b70ec..54a6042 100644 --- a/public/manifest.webmanifest +++ b/public/manifest.webmanifest @@ -1,38 +1,35 @@ { - "name": "Just a bunch of blobs", - "short_name": "Blobs", - "description": "A WebGPU agent simulation: a million blobs leave trails, infect each other across generations, and react to your brush.", - "start_url": "/", - "scope": "/", + "name": "Fleeting Garden", + "short_name": "Garden", + "description": "Tend it while you can. The garden returns to weather either way. A WebGPU drawing toy in your browser.", + "start_url": "./", + "scope": "./", "display": "fullscreen", - "display_override": ["fullscreen", "standalone", "minimal-ui"], - "orientation": "any", - "background_color": "#b7455e", - "theme_color": "#b7455e", + "background_color": "#10151f", + "theme_color": "#10151f", "icons": [ { - "src": "/favicon.svg", + "src": "favicon.svg", "sizes": "any", - "type": "image/svg+xml", - "purpose": "any" + "type": "image/svg+xml" }, { - "src": "/pwa-64x64.png", + "src": "pwa-64x64.png", "sizes": "64x64", "type": "image/png" }, { - "src": "/pwa-192x192.png", + "src": "pwa-192x192.png", "sizes": "192x192", "type": "image/png" }, { - "src": "/pwa-512x512.png", + "src": "pwa-512x512.png", "sizes": "512x512", "type": "image/png" }, { - "src": "/maskable-icon-512x512.png", + "src": "maskable-icon-512x512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" diff --git a/public/maskable-icon-512x512.png b/public/maskable-icon-512x512.png index 7e94d56..ea4b3c2 100644 Binary files a/public/maskable-icon-512x512.png and b/public/maskable-icon-512x512.png differ diff --git a/public/og-image.jpg b/public/og-image.jpg new file mode 100644 index 0000000..0ced8fc Binary files /dev/null and b/public/og-image.jpg differ diff --git a/public/pwa-192x192.png b/public/pwa-192x192.png index 667a104..e05b2d3 100644 Binary files a/public/pwa-192x192.png and b/public/pwa-192x192.png differ diff --git a/public/pwa-512x512.png b/public/pwa-512x512.png index 9a5361f..c171454 100644 Binary files a/public/pwa-512x512.png and b/public/pwa-512x512.png differ diff --git a/public/pwa-64x64.png b/public/pwa-64x64.png index 7c93311..cfe8667 100644 Binary files a/public/pwa-64x64.png and b/public/pwa-64x64.png differ diff --git a/public/robots.txt b/public/robots.txt index c2a49f4..0775d05 100644 --- a/public/robots.txt +++ b/public/robots.txt @@ -1,2 +1,4 @@ User-agent: * Allow: / + +Sitemap: https://schmelczer.dev/fleeting/sitemap.xml diff --git a/public/sitemap.xml b/public/sitemap.xml new file mode 100644 index 0000000..d349665 --- /dev/null +++ b/public/sitemap.xml @@ -0,0 +1,6 @@ + + + + https://schmelczer.dev/fleeting/ + + diff --git a/src/analytics.ts b/src/analytics.ts new file mode 100644 index 0000000..b19958a --- /dev/null +++ b/src/analytics.ts @@ -0,0 +1,68 @@ +import { + init as plausibleInit, + track as plausibleTrack, + type PlausibleEventOptions, +} from '@plausible-analytics/tracker'; + +import { appConfig } from './config'; +import type { VibeId } from './vibes'; + +let isInitialized = false; + +const track = (eventName: string, options: PlausibleEventOptions = {}) => { + try { + plausibleTrack(eventName, options); + } catch (error) { + console.warn(`Could not track analytics event "${eventName}".`, error); + } +}; + +export const initAnalytics = () => { + if (isInitialized) { + return; + } + + try { + plausibleInit({ + domain: appConfig.analytics.domain, + endpoint: appConfig.analytics.endpoint, + autoCapturePageviews: appConfig.analytics.autoCapturePageviews, + logging: appConfig.analytics.logging, + }); + isInitialized = true; + } catch (error) { + console.warn('Could not initialize analytics.', error); + } +}; + +export const trackVibeChange = ({ + vibeId, + vibeName, + source, +}: { + vibeId: VibeId; + vibeName: string; + source: string; +}) => { + track('Vibe Change', { + props: { + vibeId, + vibeName, + source, + }, + }); +}; + +export const trackStart = () => { + track('Start'); +}; + +export const trackExport = ({ vibeId }: { vibeId: VibeId }) => { + track('Export', { + props: { + format: 'png', + resolution: 'internal-buffer', + vibeId, + }, + }); +};