diff --git a/group_vars/all/09_networks.yml b/group_vars/all/09_networks.yml
index 7e889504..940fc03b 100644
--- a/group_vars/all/09_networks.yml
+++ b/group_vars/all/09_networks.yml
@@ -120,6 +120,8 @@ defaults_networks:
subnet: 192.168.104.96/28
web-app-suitecrm:
subnet: 192.168.104.112/28
+ web-app-littlejs:
+ subnet: 192.168.104.128/28
# /24 Networks / 254 Usable Clients
web-app-bigbluebutton:
diff --git a/group_vars/all/10_ports.yml b/group_vars/all/10_ports.yml
index 8cf87b04..957a28a0 100644
--- a/group_vars/all/10_ports.yml
+++ b/group_vars/all/10_ports.yml
@@ -85,6 +85,7 @@ ports:
web-app-shopware: 8060
web-svc-onlyoffice: 8061
web-app-suitecrm: 8062
+ web-app-littlejs: 8063
web-app-bigbluebutton: 48087 # This port is predefined by bbb. @todo Try to change this to a 8XXX port
public:
# The following ports should be changed to 22 on the subdomain via stream mapping
diff --git a/roles/web-app-littlejs/README.md b/roles/web-app-littlejs/README.md
new file mode 100644
index 00000000..2491d120
--- /dev/null
+++ b/roles/web-app-littlejs/README.md
@@ -0,0 +1,33 @@
+# LittleJS Playground
+
+## Description
+
+**LittleJS Playground** is a self-hosted web application that bundles the LittleJS engine, its official examples, and a minimal Infinito.Nexus launcher UI.
+It provides a simple, tile-based overview of demos and games, allowing you to quickly explore LittleJS examples directly in your browser.
+
+## Overview
+
+LittleJS Playground is designed as a lightweight HTML5 game sandbox for education, prototyping, and fun.
+It exposes the original LittleJS `examples/` browser and adds a Bootstrap-based landing page that lists all examples as clickable tiles and offers quick links to popular games such as platformers and arcade-style demos.
+The app runs as a single Docker container and requires no additional database or backend services.
+
+## Features
+
+- **Self-hosted LittleJS environment** — run LittleJS demos and games under your own domain.
+- **Example browser integration** — direct access to the original LittleJS example browser.
+- **Tile-based launcher UI** — dynamically renders a catalog from the `exampleList` definition.
+- **Quick links for games** — navbar entries for selected games (e.g. platformer, pong, space shooter).
+- **Bootstrap-styled interface** — clean, minimalistic, and responsive layout.
+- **Docker-ready** — fully integrated into the Infinito.Nexus Docker stack.
+
+## Further Resources
+
+- 🕹 Upstream engine & examples: [KilledByAPixel/LittleJS](https://github.com/KilledByAPixel/LittleJS)
+- 📚 LittleJS README & docs: [GitHub – LittleJS](https://github.com/KilledByAPixel/LittleJS#readme)
+
+## Credits
+
+LittleJS is developed and maintained by **KilledByAPixel** and contributors.
+This integration and role are developed and maintained by **Kevin Veen-Birkenbach**.
+Learn more at [veen.world](https://www.veen.world).
+Licensed under the [Infinito.Nexus NonCommercial License](https://s.infinito.nexus/license).
diff --git a/roles/web-app-littlejs/config/main.yml b/roles/web-app-littlejs/config/main.yml
new file mode 100644
index 00000000..bc481686
--- /dev/null
+++ b/roles/web-app-littlejs/config/main.yml
@@ -0,0 +1,33 @@
+features:
+ matomo: true
+ css: true
+ desktop: true
+ logout: false
+
+server:
+ csp:
+ whitelist:
+ script-src-elem:
+ - "https://cdnjs.cloudflare.com"
+ - "https://cdn.jsdelivr.net"
+ style-src-elem:
+ - "https://cdn.jsdelivr.net"
+ - "https://cdnjs.cloudflare.com"
+ font-src:
+ - "https://cdnjs.cloudflare.com"
+ - "data:"
+ connect-src:
+ - "https://cdn.jsdelivr.net"
+ frame-src:
+ - "https://littlejs.{{ PRIMARY_DOMAIN }}"
+ flags:
+ style-src-attr:
+ unsafe-inline: true
+ script-src-elem:
+ unsafe-inline: true
+ script-src-attr:
+ unsafe-inline: true
+ domains:
+ canonical:
+ - "littlejs.{{ PRIMARY_DOMAIN }}"
+ aliases: []
diff --git a/roles/web-app-littlejs/files/style.css b/roles/web-app-littlejs/files/style.css
new file mode 100644
index 00000000..2d91c01b
--- /dev/null
+++ b/roles/web-app-littlejs/files/style.css
@@ -0,0 +1,55 @@
+/* Global Base Styling */
+body {
+ /* Use Infinito.Nexus color scheme variables */
+ background-color: var(--color-01-90);
+ color: var(--color-01-05);
+ min-height: 100vh;
+ padding-bottom: 4rem; /* Space for footer bar */
+}
+
+/* Card Styling (Infinito.Nexus Homepage Style) */
+.app-card {
+ border-radius: 1rem;
+ background: radial-gradient(
+ circle at top left,
+ rgba(var(--color-rgb-01-10), 0.95),
+ rgba(var(--color-rgb-01-20), 0.90)
+ );
+ border: 1px solid rgba(var(--color-rgb-01-40), 0.35);
+ box-shadow: 0 18px 45px rgba(var(--color-rgb-01-90), 0.65);
+ transition: transform .12s ease-out, box-shadow .12s ease-out, border-color .12s ease-out;
+}
+
+.app-card:hover {
+ transform: translateY(-3px);
+ box-shadow: 0 22px 60px rgba(var(--color-rgb-01-90), 0.90);
+ border-color: rgba(var(--color-rgb-01-30), 0.70);
+}
+
+.app-icon {
+ font-size: 2.6rem;
+}
+
+.app-badge {
+ font-size: 0.7rem;
+ text-transform: uppercase;
+ letter-spacing: .12em;
+}
+
+/* Footer Bar */
+.footer-bar {
+ position: fixed;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ z-index: 1030;
+}
+
+.footer-bar .btn-link {
+ text-decoration: none;
+ font-size: 0.8rem;
+}
+
+.footer-bar .btn-link:hover {
+ text-decoration: underline;
+}
diff --git a/roles/web-app-littlejs/meta/main.yml b/roles/web-app-littlejs/meta/main.yml
new file mode 100644
index 00000000..c9b6087c
--- /dev/null
+++ b/roles/web-app-littlejs/meta/main.yml
@@ -0,0 +1,28 @@
+# roles/web-app-littlejs/meta/main.yml
+galaxy_info:
+ author: "Kevin Veen-Birkenbach"
+ description: >
+ LittleJS Playground – self-hosted LittleJS example browser and
+ collection of HTML5 games and demos, wrapped as a Dockerized web app.
+ license: "Infinito.Nexus NonCommercial License"
+ license_url: "https://s.infinito.nexus/license"
+ company: |
+ Kevin Veen-Birkenbach
+ Consulting & Coaching Solutions
+ https://www.veen.world
+ galaxy_tags:
+ - infinito
+ - docker
+ - webapp
+ - game
+ - html5
+ - littlejs
+ repository: "https://s.infinito.nexus/code"
+ issue_tracker_url: "https://s.infinito.nexus/issues"
+ documentation: "https://docs.infinito.nexus"
+ logo:
+ class: "fa-solid fa-gamepad"
+ run_after:
+ - web-app-matomo
+
+dependencies: []
diff --git a/roles/web-app-littlejs/tasks/01_core.yml b/roles/web-app-littlejs/tasks/01_core.yml
new file mode 100644
index 00000000..b80bcd06
--- /dev/null
+++ b/roles/web-app-littlejs/tasks/01_core.yml
@@ -0,0 +1,23 @@
+- name: "load docker, proxy for '{{ application_id }}'"
+ include_role:
+ name: sys-stk-full-stateless
+ vars:
+ docker_compose_flush_handlers: false
+
+- name: "Load LittleJS example metadata"
+ include_vars:
+ file: "examples.yml"
+
+- name: "Render LittleJS index.html"
+ template:
+ src: "html/index.html.j2"
+ dest: "{{ LITTLEJS_INDEX_HOST_ABS }}"
+ mode: "0644"
+ notify:
+ - docker compose build
+ - docker compose up
+
+- name: "flush docker compose for '{{ application_id }}'"
+ meta: flush_handlers
+
+- include_tasks: utils/run_once.yml
diff --git a/roles/web-app-littlejs/tasks/main.yml b/roles/web-app-littlejs/tasks/main.yml
new file mode 100644
index 00000000..4204121d
--- /dev/null
+++ b/roles/web-app-littlejs/tasks/main.yml
@@ -0,0 +1,3 @@
+---
+- include_tasks: 01_core.yml
+ when: run_once_web_app_littlejs is not defined
diff --git a/roles/web-app-littlejs/templates/Dockerfile.j2 b/roles/web-app-littlejs/templates/Dockerfile.j2
new file mode 100644
index 00000000..f1abfbee
--- /dev/null
+++ b/roles/web-app-littlejs/templates/Dockerfile.j2
@@ -0,0 +1,9 @@
+# roles/web-app-littlejs/templates/Dockerfile.j2
+FROM nginx:alpine
+
+# Static LittleJS assets – engine builds + examples
+COPY ./{{ LITTLEJS_APP_REL }}/examples /usr/share/nginx/html/examples
+COPY ./{{ LITTLEJS_APP_REL }}/dist /usr/share/nginx/html/dist
+
+# Custom Infinito.Nexus landing page with Bootstrap menu and tiles
+COPY ./{{ LITTLEJS_INDEX_HOST_REL }} /usr/share/nginx/html/index.html
diff --git a/roles/web-app-littlejs/templates/docker-compose.yml.j2 b/roles/web-app-littlejs/templates/docker-compose.yml.j2
new file mode 100644
index 00000000..0c078a0b
--- /dev/null
+++ b/roles/web-app-littlejs/templates/docker-compose.yml.j2
@@ -0,0 +1,10 @@
+---
+{% include 'roles/docker-compose/templates/base.yml.j2' %}
+ {{ LITTLEJS_SERVICE }}:
+ container_name: {{ LITTLEJS_CONTAINER }}
+ {{ lookup('template', 'roles/docker-container/templates/build.yml.j2') | indent(4) }}
+ ports:
+ - 127.0.0.1:{{ ports.localhost.http[application_id] }}:80
+{% include 'roles/docker-container/templates/base.yml.j2' %}
+
+{% include 'roles/docker-compose/templates/networks.yml.j2' %}
diff --git a/roles/web-app-littlejs/templates/html/index.html.j2 b/roles/web-app-littlejs/templates/html/index.html.j2
new file mode 100644
index 00000000..40073814
--- /dev/null
+++ b/roles/web-app-littlejs/templates/html/index.html.j2
@@ -0,0 +1,32 @@
+
+
+
+
+ {{ LITTLEJS_TITLE }}
+
+
+
+
+
+
+
+
+
+
+ {% include "nav_top.html.j2" %}
+
+ {% include "main.html.j2" %}
+
+ {% include "nav_bottom.html.j2" %}
+
+
+
+
\ No newline at end of file
diff --git a/roles/web-app-littlejs/templates/html/main.html.j2 b/roles/web-app-littlejs/templates/html/main.html.j2
new file mode 100644
index 00000000..b7a5da57
--- /dev/null
+++ b/roles/web-app-littlejs/templates/html/main.html.j2
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+ {% for key, cat in littlejs_examples|dictsort %}
+ {% for ex in cat['items'] %}
+ {% if ex.is_project %}
+ {% set href = "/examples/" ~ ex.file ~ "/" %}
+ {% else %}
+ {% set href = "/examples/shorts/base.html?file=" ~ ex.file %}
+ {% endif %}
+
+
+
+
+
+
+
+
+
+
+ {{ cat.label|replace('---','')|trim }}
+
+
+
+
{{ ex.name }}
+
+ {% if ex.description %}
+
{{ ex.description }}
+ {% endif %}
+
+ {% if ex.tags %}
+
{{ ex.tags }}
+ {% endif %}
+
+
+
+
+
+
+
+
+
+ {% endfor %}
+ {% endfor %}
+
+
+
+
diff --git a/roles/web-app-littlejs/templates/html/nav_bottom.html.j2 b/roles/web-app-littlejs/templates/html/nav_bottom.html.j2
new file mode 100644
index 00000000..709d70be
--- /dev/null
+++ b/roles/web-app-littlejs/templates/html/nav_bottom.html.j2
@@ -0,0 +1,23 @@
+
+
+
diff --git a/roles/web-app-littlejs/templates/html/nav_top.html.j2 b/roles/web-app-littlejs/templates/html/nav_top.html.j2
new file mode 100644
index 00000000..fe9d07f5
--- /dev/null
+++ b/roles/web-app-littlejs/templates/html/nav_top.html.j2
@@ -0,0 +1,68 @@
+
+
diff --git a/roles/web-app-littlejs/vars/examples.yml b/roles/web-app-littlejs/vars/examples.yml
new file mode 100644
index 00000000..9c914e13
--- /dev/null
+++ b/roles/web-app-littlejs/vars/examples.yml
@@ -0,0 +1,401 @@
+# roles/web-app-littlejs/vars/examples.yml
+# LittleJS examples for launcher UI (used to generate example tiles and menu)
+
+littlejs_examples:
+ basic:
+ label: "--- BASIC ---"
+ icon: "fa-solid fa-shapes"
+ items:
+ - name: "Hello World"
+ file: "helloWorld.js"
+ description: "Simple starter example"
+ is_project: false
+ tags: "beginner, gradient, text, tiles"
+ icon: "fa-solid fa-seedling"
+
+ - name: "Empty"
+ file: "empty.js"
+ description: "Empty example template"
+ is_project: false
+ tags: "beginner, text"
+ icon: "fa-regular fa-file"
+
+ - name: "Texture"
+ file: "texture.js"
+ description: "Texture display and manipulation"
+ is_project: false
+ tags: "sprites, loading, tiles"
+ icon: "fa-regular fa-image"
+
+ - name: "Animation"
+ file: "animation.js"
+ description: "Sprite animation system"
+ is_project: false
+ tags: "frames, loop, tiles, sprites"
+ icon: "fa-solid fa-film"
+
+ - name: "Shapes"
+ file: "shapes.js"
+ description: "Draw geometric shapes and primitives"
+ is_project: false
+ tags: "circle, ellipse, rectangle, polygon, lines"
+ icon: "fa-solid fa-draw-polygon"
+
+ - name: "Colors"
+ file: "colors.js"
+ description: "Color manipulation and HSL"
+ is_project: false
+ tags: "hue, saturation, blending, rectangle"
+ icon: "fa-solid fa-palette"
+
+ - name: "Sprite Atlas"
+ file: "spriteAtlas.js"
+ description: "Sprite atlas and tile rendering"
+ is_project: false
+ tags: "sheet, frames, tiles"
+ icon: "fa-solid fa-table-cells-large"
+
+ - name: "Blending"
+ file: "blending.js"
+ description: "Additive blending and transparency"
+ is_project: false
+ tags: "alpha, color, tiles, smooth"
+ icon: "fa-solid fa-droplet"
+
+ - name: "Tile Layer"
+ file: "tileLayer.js"
+ description: "Tile layer rendering system"
+ is_project: false
+ tags: "level, map, grid, particles"
+ icon: "fa-solid fa-border-all"
+
+ - name: "Particles"
+ file: "particles.js"
+ description: "Particle system"
+ is_project: false
+ tags: "effects, emitter, physics, fire, smoke"
+ icon: "fa-solid fa-burst"
+
+ - name: "Input"
+ file: "input.js"
+ description: "Input system demo for keyboard, mouse, touch, and gamepad."
+ is_project: false
+ tags: "input, control"
+ icon: "fa-regular fa-keyboard"
+
+ - name: "Timers"
+ file: "timers.js"
+ description: "Timer objects and UI"
+ is_project: false
+ tags: "delay, interval, slider"
+ icon: "fa-regular fa-clock"
+
+ - name: "Sound Effects"
+ file: "sound.js"
+ description: "ZzFX sound effect generator"
+ is_project: false
+ tags: "audio, volume, ui"
+ icon: "fa-solid fa-bell"
+
+ - name: "Music"
+ file: "music.js"
+ description: "Basic load, play, pause, and stop"
+ is_project: false
+ tags: "music, sound, audio, streaming, volume, ui"
+ icon: "fa-solid fa-headphones-simple"
+
+ - name: "Video"
+ file: "videoPlayer.js"
+ description: "Basic video play, pause, and stop"
+ is_project: false
+ tags: "movie, sound, audio, streaming, volume, ui"
+ icon: "fa-solid fa-video"
+
+ - name: "Font Image"
+ file: "fontImage.js"
+ description: "Bitmap font system with built-in system font"
+ is_project: false
+ tags: "text, characters"
+ icon: "fa-solid fa-font"
+
+ - name: "Medals"
+ file: "medals.js"
+ description: "Achievement system"
+ is_project: false
+ tags: "unlock, progress, newgrounds"
+ icon: "fa-solid fa-medal"
+
+ - name: "Tile Raycast"
+ file: "tileRaycast.js"
+ description: "Example of the tile layer raycast collision"
+ is_project: false
+ tags: "level, map, grid"
+ icon: "fa-solid fa-crosshairs"
+
+ - name: "Camera Mouse Drag"
+ file: "cameraDrag.js"
+ description: "Example of how to control camera with mouse"
+ is_project: false
+ tags: "ui, input, control"
+ icon: "fa-solid fa-arrows-up-down-left-right"
+
+ - name: "Debug Drawing"
+ file: "debugDraw.js"
+ description: "Debug drawing system"
+ is_project: false
+ tags: "debug, circle, line, rectangle"
+ icon: "fa-solid fa-bug"
+
+ advanced:
+ label: "--- ADVANCED ---"
+ icon: "fa-solid fa-rocket"
+ items:
+ - name: "Clock"
+ file: "clock.js"
+ description: "Animated analog clock"
+ is_project: false
+ tags: "time, rotation, lines, rectangle"
+ icon: "fa-regular fa-clock"
+
+ - name: "Starfield"
+ file: "starfield.js"
+ description: "Animated parallax starfield"
+ is_project: false
+ tags: "space, movement, depth, rectangle"
+ icon: "fa-solid fa-star"
+
+ - name: "Parallax"
+ file: "parallax.js"
+ description: "Parallax scrolling mountains"
+ is_project: false
+ tags: "generative, canvas, background"
+ icon: "fa-solid fa-mountain-sun"
+
+ - name: "Maze Generator"
+ file: "maze.js"
+ description: "Procedural maze generation"
+ is_project: false
+ tags: "generative, level, tiles, map, grid"
+ icon: "fa-solid fa-border-style"
+
+ - name: "Piano"
+ file: "piano.js"
+ description: "Interactive piano keyboard"
+ is_project: false
+ tags: "music, sound, audio, notes, ui, instrument"
+ icon: "fa-solid fa-music"
+
+ - name: "Step Sequencer"
+ file: "sequencer.js"
+ description: "Simple music loop creation tool"
+ is_project: false
+ tags: "music, sound, audio, notes, ui, instrument"
+ icon: "fa-solid fa-sliders"
+
+ - name: "Music Player"
+ file: "musicPlayer.js"
+ description: "Music player with audio seeking and drag and drop"
+ is_project: false
+ tags: "sound, loading, audio, ui"
+ icon: "fa-solid fa-compact-disc"
+
+ games:
+ label: "--- GAMES ---"
+ icon: "fa-solid fa-gamepad"
+ items:
+ - name: "Pong Game"
+ file: "pongGame.js"
+ description: "Classic paddle ball bouncing"
+ is_project: false
+ tags: "objects, collision"
+ icon: "fa-solid fa-table-tennis-paddle-ball"
+
+ - name: "Platformer Game"
+ file: "platformer.js"
+ description: "Jump and run side view"
+ is_project: false
+ tags: "objects, gravity, level, tiles, camera"
+ icon: "fa-solid fa-person-running"
+
+ - name: "Top Down Game"
+ file: "topDown.js"
+ description: "Top-down style camera"
+ is_project: false
+ tags: "objects, movement, exploration"
+ icon: "fa-solid fa-up-down-left-right"
+
+ - name: "Tilted View Game"
+ file: "tiltedView.js"
+ description: "Pseudo-3D oblique view"
+ is_project: false
+ tags: "isometric, depth"
+ icon: "fa-solid fa-cube"
+
+ - name: "Flappy Game"
+ file: "flappyGame.js"
+ description: "Flappy bird style game"
+ is_project: false
+ tags: "objects, obstacles"
+ icon: "fa-solid fa-dove"
+
+ - name: "Lander Game"
+ file: "landerGame.js"
+ description: "Lunar lander style game"
+ is_project: false
+ tags: "objects, physics"
+ icon: "fa-solid fa-rocket"
+
+ - name: "Hill Glide Game"
+ file: "hillGlideGame.js"
+ description: "Tiny wings style sliding game"
+ is_project: false
+ tags: "objects, physics, speed"
+ icon: "fa-solid fa-mountain"
+
+ - name: "Sliding Puzzle"
+ file: "slidingPuzzle.js"
+ description: "15 tile sliding puzzle"
+ is_project: false
+ tags: "objects, numbers, ui"
+ icon: "fa-solid fa-grip"
+
+ - name: "Space Game"
+ file: "spaceGame.js"
+ description: "Spaceship shooter with parallax"
+ is_project: false
+ tags: "objects, weapons, stars, camera, rotation"
+ icon: "fa-solid fa-meteor"
+
+ - name: "3D FPS Game"
+ file: "fps.js"
+ description: "Pseudo 3D raycasting demo"
+ is_project: false
+ tags: "3D, FPS, maze, camera"
+ icon: "fa-solid fa-bullseye"
+
+ plugins:
+ label: "--- PLUGINS ---"
+ icon: "fa-solid fa-puzzle-piece"
+ items:
+ - name: "Box2d Demo"
+ file: "box2d.js"
+ description: "Box2D physics plugin"
+ is_project: false
+ tags: "objects, mouse"
+ icon: "fa-solid fa-cubes"
+
+ - name: "Box2d Car"
+ file: "box2dCar.js"
+ description: "Drivable car with Box2D physics"
+ is_project: false
+ tags: "objects, vehicle, suspension, wheels"
+ icon: "fa-solid fa-car"
+
+ - name: "Box2d Pool"
+ file: "box2dPool.js"
+ description: "Billiard table pool game with Box2d physics"
+ is_project: false
+ tags: "objects, game"
+ icon: "fa-solid fa-pool-8-ball"
+
+ - name: "Box2d Tile Layer"
+ file: "box2dTileLayer.js"
+ description: "Tile layer with Box2d physics"
+ is_project: false
+ tags: "objects, level, map, grid"
+ icon: "fa-solid fa-border-all"
+
+ - name: "WebGL Shader"
+ file: "shader.js"
+ description: "Full canvas webgl shader"
+ is_project: false
+ tags: "webgl, visual, effect"
+ icon: "fa-solid fa-wand-magic-sparkles"
+
+ - name: "Post Processing"
+ file: "postProcess.js"
+ description: "Shader effects and filters"
+ is_project: false
+ tags: "webgl, visual, effect"
+ icon: "fa-solid fa-sparkles"
+
+ - name: "UI System"
+ file: "uiSystem.js"
+ description: "Buttons, sliders, and checkboxes"
+ is_project: false
+ tags: "objects, widgets, interactive"
+ icon: "fa-solid fa-sliders"
+
+ - name: "Nine Slice"
+ file: "nineSlice.js"
+ description: "Scalable UI panels"
+ is_project: false
+ tags: "three slice, stretch, corners, text, tiles"
+ icon: "fa-solid fa-vector-square"
+
+ full_examples:
+ label: "--- FULL EXAMPLES ---"
+ icon: "fa-solid fa-box-open"
+ items:
+ - name: "Starter"
+ file: "starter"
+ description: "Clean project template"
+ is_project: true
+ tags: "base, empty, particles"
+ icon: "fa-solid fa-seedling"
+
+ - name: "Breakout Tutorial"
+ file: "breakoutTutorial"
+ description: "Step-by-step breakout game tutorial"
+ is_project: true
+ tags: "objects, physics, score"
+ icon: "fa-solid fa-graduation-cap"
+
+ - name: "Breakout Game"
+ file: "breakout"
+ description: "Complete breakout game"
+ is_project: true
+ tags: "objects, physics, score"
+ icon: "fa-solid fa-table-tennis-paddle-ball"
+
+ - name: "Platforming Game"
+ file: "platformer"
+ description: "Platformer with level loading"
+ is_project: true
+ tags: "jump, world, tiles, pixel art, sprites"
+ icon: "fa-solid fa-person-running"
+
+ - name: "Puzzle Game"
+ file: "puzzle"
+ description: "Match 3 style puzzle game"
+ is_project: true
+ tags: "match, swap, sprites"
+ icon: "fa-solid fa-puzzle-piece"
+
+ - name: "Stress Test"
+ file: "stress"
+ description: "Performance and music test"
+ is_project: true
+ tags: "optimization, tiles, sprites"
+ icon: "fa-solid fa-gauge-high"
+
+ - name: "Box2D Plugin"
+ file: "box2d"
+ description: "Full Box2D physics demo"
+ is_project: true
+ tags: "objects, bodies, joints"
+ icon: "fa-solid fa-cubes"
+
+ - name: "HTML Menus"
+ file: "htmlMenu"
+ description: "HTML UI integration"
+ is_project: true
+ tags: "web, browser, overlay, button, slider, textbox"
+ icon: "fa-solid fa-code"
+
+ - name: "UI System Plugin Demo"
+ file: "uiSystem"
+ description: "Complete UI system demo"
+ is_project: true
+ tags: "menu, overlay, button, slider, checkbox"
+ icon: "fa-solid fa-sliders"
diff --git a/roles/web-app-littlejs/vars/main.yml b/roles/web-app-littlejs/vars/main.yml
new file mode 100644
index 00000000..8ef145e7
--- /dev/null
+++ b/roles/web-app-littlejs/vars/main.yml
@@ -0,0 +1,30 @@
+# General
+application_id: "web-app-littlejs"
+entity_name: "{{ application_id | get_entity_name }}"
+
+# Features
+features:
+ css: true
+ matomo: true
+ desktop: true
+
+LITTLEJS_HEADLINE: ""
+
+LITTLEJS_TITLE: "LittleJS Playground – {{ PRIMARY_DOMAIN | upper }}"
+
+# Base repository URL for LittleJS
+LITTLEJS_REPOSITORY_BASE: "https://github.com/KilledByAPixel/LittleJS"
+
+# Git repository for LittleJS engine + examples
+docker_repository_address: "{{ LITTLEJS_REPOSITORY_BASE }}.git"
+docker_pull_git_repository: true
+docker_repository_branch: "main"
+
+# Relative path where sys-stk-full-stateless checks out the repo
+LITTLEJS_APP_REL: "services/repository"
+LITTLEJS_INDEX_HOST_ABS: "{{ [ docker_compose.directories.volumes, 'index.html' ] | path_join }}"
+LITTLEJS_INDEX_HOST_REL: "volumes/index.html"
+
+# Helper variables
+LITTLEJS_CONTAINER: "{{ entity_name }}"
+LITTLEJS_SERVICE: "{{ entity_name }}"