diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml new file mode 100644 index 0000000..a52d988 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -0,0 +1,55 @@ +name: Bug report +description: Report a reproducible problem in an experience or its source. +title: "[Bug]: " +labels: + - bug +body: + - type: markdown + attributes: + value: Thanks for helping improve Mint Playground. Please keep security reports private as described in SECURITY.md. + - type: input + id: experience + attributes: + label: Experience slug + description: The directory name or live-demo slug, such as vanguard-protocol. + placeholder: vanguard-protocol + validations: + required: true + - type: textarea + id: problem + attributes: + label: What happened? + description: Describe the behavior and what you expected instead. + validations: + required: true + - type: textarea + id: reproduction + attributes: + label: Reproduction steps + placeholder: | + 1. Open … + 2. Select … + 3. Observe … + validations: + required: true + - type: input + id: environment + attributes: + label: Browser and device + placeholder: Chrome 140 on macOS 16, or Safari on iPhone 18 + validations: + required: true + - type: textarea + id: evidence + attributes: + label: Screenshots, recordings, or console output + description: Remove tokens, personal data, and other sensitive information before posting. + - type: checkboxes + id: checks + attributes: + label: Checks + options: + - label: I searched existing issues for this problem. + required: true + - label: This is not a security vulnerability. + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..797a1e7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Mint MCP documentation + url: https://mcp.mint.gg/docs + about: Get help installing or using Mint MCP. + - name: Play the experiences + url: https://play.mint.gg + about: Confirm whether a problem also appears in the deployed experience. diff --git a/.github/ISSUE_TEMPLATE/experience-request.yml b/.github/ISSUE_TEMPLATE/experience-request.yml new file mode 100644 index 0000000..d4bc532 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/experience-request.yml @@ -0,0 +1,45 @@ +name: Experience request +description: Suggest a 3D experience or reusable pattern for a future Playground drop. +title: "[Experience]: " +labels: + - enhancement +body: + - type: textarea + id: concept + attributes: + label: Concept + description: What should someone be able to do, and why is it valuable as an open-source reference? + validations: + required: true + - type: dropdown + id: category + attributes: + label: Category + options: + - Browser game + - Product or commerce + - Data visualization or education + - Creative or editorial + - World or simulation + - Reusable Three.js pattern + validations: + required: true + - type: textarea + id: learning + attributes: + label: What should developers learn from it? + description: List the interaction, rendering, architecture, or performance patterns it would demonstrate. + validations: + required: true + - type: textarea + id: references + attributes: + label: References + description: Link to relevant open-source projects or original visual references. Do not attach unlicensed media. + - type: checkboxes + id: rights + attributes: + label: Rights check + options: + - label: The proposal does not depend on third-party characters, brands, or media that Mint lacks permission to publish. + required: true diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..21a4bdd --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,21 @@ +## What changed + + + +## Experience + + + +## Verification + +- [ ] `pnpm validate:catalog` +- [ ] The affected experience builds successfully +- [ ] Relevant tests pass +- [ ] I checked keyboard, touch, and reduced-motion behavior where applicable +- [ ] I included before-and-after captures for visual changes + +## Contribution checks + +- [ ] This change is focused and does not reformat unrelated files +- [ ] I have permission to publish all contributed code and media +- [ ] No credentials, production data, or downloaded Mint CDN assets are included diff --git a/.github/assets/experiences/boulder-cove.webp b/.github/assets/experiences/boulder-cove.webp new file mode 100644 index 0000000..cb52d2d Binary files /dev/null and b/.github/assets/experiences/boulder-cove.webp differ diff --git a/.github/assets/experiences/breakline.webp b/.github/assets/experiences/breakline.webp new file mode 100644 index 0000000..825b09a Binary files /dev/null and b/.github/assets/experiences/breakline.webp differ diff --git a/.github/assets/experiences/cdx-2089-music-player.webp b/.github/assets/experiences/cdx-2089-music-player.webp new file mode 100644 index 0000000..083be6b Binary files /dev/null and b/.github/assets/experiences/cdx-2089-music-player.webp differ diff --git a/.github/assets/experiences/clicky.webp b/.github/assets/experiences/clicky.webp new file mode 100644 index 0000000..8ac3e40 Binary files /dev/null and b/.github/assets/experiences/clicky.webp differ diff --git a/.github/assets/experiences/common-form.webp b/.github/assets/experiences/common-form.webp new file mode 100644 index 0000000..e99b50c Binary files /dev/null and b/.github/assets/experiences/common-form.webp differ diff --git a/.github/assets/experiences/complete-shelf.webp b/.github/assets/experiences/complete-shelf.webp new file mode 100644 index 0000000..b8f09d3 Binary files /dev/null and b/.github/assets/experiences/complete-shelf.webp differ diff --git a/.github/assets/experiences/compound-visualization.webp b/.github/assets/experiences/compound-visualization.webp new file mode 100644 index 0000000..f7cd5a2 Binary files /dev/null and b/.github/assets/experiences/compound-visualization.webp differ diff --git a/.github/assets/experiences/dead-reckoning.webp b/.github/assets/experiences/dead-reckoning.webp new file mode 100644 index 0000000..1ef88dd Binary files /dev/null and b/.github/assets/experiences/dead-reckoning.webp differ diff --git a/.github/assets/experiences/dynasty-index.webp b/.github/assets/experiences/dynasty-index.webp new file mode 100644 index 0000000..bbaaf95 Binary files /dev/null and b/.github/assets/experiences/dynasty-index.webp differ diff --git a/.github/assets/experiences/elemental-chess.webp b/.github/assets/experiences/elemental-chess.webp new file mode 100644 index 0000000..da4974d Binary files /dev/null and b/.github/assets/experiences/elemental-chess.webp differ diff --git a/.github/assets/experiences/fieldline-santa-clara.webp b/.github/assets/experiences/fieldline-santa-clara.webp new file mode 100644 index 0000000..0a6ba31 Binary files /dev/null and b/.github/assets/experiences/fieldline-santa-clara.webp differ diff --git a/.github/assets/experiences/final-whistle.webp b/.github/assets/experiences/final-whistle.webp new file mode 100644 index 0000000..07576ad Binary files /dev/null and b/.github/assets/experiences/final-whistle.webp differ diff --git a/.github/assets/experiences/financial-city.webp b/.github/assets/experiences/financial-city.webp new file mode 100644 index 0000000..7ca8cf4 Binary files /dev/null and b/.github/assets/experiences/financial-city.webp differ diff --git a/.github/assets/experiences/flappy-bird-3d.webp b/.github/assets/experiences/flappy-bird-3d.webp new file mode 100644 index 0000000..683346c Binary files /dev/null and b/.github/assets/experiences/flappy-bird-3d.webp differ diff --git a/.github/assets/experiences/forgotten-experiment.webp b/.github/assets/experiences/forgotten-experiment.webp new file mode 100644 index 0000000..97ba7fc Binary files /dev/null and b/.github/assets/experiences/forgotten-experiment.webp differ diff --git a/.github/assets/experiences/four-fields.webp b/.github/assets/experiences/four-fields.webp new file mode 100644 index 0000000..5b5d8b7 Binary files /dev/null and b/.github/assets/experiences/four-fields.webp differ diff --git a/.github/assets/experiences/helios-form.webp b/.github/assets/experiences/helios-form.webp new file mode 100644 index 0000000..9d041eb Binary files /dev/null and b/.github/assets/experiences/helios-form.webp differ diff --git a/.github/assets/experiences/impossible-places.webp b/.github/assets/experiences/impossible-places.webp new file mode 100644 index 0000000..b85e6c3 Binary files /dev/null and b/.github/assets/experiences/impossible-places.webp differ diff --git a/.github/assets/experiences/jimothys-backyard-dash.webp b/.github/assets/experiences/jimothys-backyard-dash.webp new file mode 100644 index 0000000..e5a1774 Binary files /dev/null and b/.github/assets/experiences/jimothys-backyard-dash.webp differ diff --git a/.github/assets/experiences/kirby-smash.webp b/.github/assets/experiences/kirby-smash.webp new file mode 100644 index 0000000..0325c2a Binary files /dev/null and b/.github/assets/experiences/kirby-smash.webp differ diff --git a/.github/assets/experiences/memory-constellation.webp b/.github/assets/experiences/memory-constellation.webp new file mode 100644 index 0000000..8fd3762 Binary files /dev/null and b/.github/assets/experiences/memory-constellation.webp differ diff --git a/.github/assets/experiences/mint-hoops.webp b/.github/assets/experiences/mint-hoops.webp new file mode 100644 index 0000000..c63030d Binary files /dev/null and b/.github/assets/experiences/mint-hoops.webp differ diff --git a/.github/assets/experiences/mint-putt.webp b/.github/assets/experiences/mint-putt.webp new file mode 100644 index 0000000..dfc6246 Binary files /dev/null and b/.github/assets/experiences/mint-putt.webp differ diff --git a/.github/assets/experiences/mossbound.webp b/.github/assets/experiences/mossbound.webp new file mode 100644 index 0000000..b1e4bff Binary files /dev/null and b/.github/assets/experiences/mossbound.webp differ diff --git a/.github/assets/experiences/neon-relic.webp b/.github/assets/experiences/neon-relic.webp new file mode 100644 index 0000000..f60d1b4 Binary files /dev/null and b/.github/assets/experiences/neon-relic.webp differ diff --git a/.github/assets/experiences/orbit-week.webp b/.github/assets/experiences/orbit-week.webp new file mode 100644 index 0000000..9a63dcf Binary files /dev/null and b/.github/assets/experiences/orbit-week.webp differ diff --git a/.github/assets/experiences/periodic-table.webp b/.github/assets/experiences/periodic-table.webp new file mode 100644 index 0000000..5019ed4 Binary files /dev/null and b/.github/assets/experiences/periodic-table.webp differ diff --git a/.github/assets/experiences/pogo-man.webp b/.github/assets/experiences/pogo-man.webp new file mode 100644 index 0000000..f1a1d09 Binary files /dev/null and b/.github/assets/experiences/pogo-man.webp differ diff --git a/.github/assets/experiences/project-forge-5.webp b/.github/assets/experiences/project-forge-5.webp new file mode 100644 index 0000000..b55e062 Binary files /dev/null and b/.github/assets/experiences/project-forge-5.webp differ diff --git a/.github/assets/experiences/quadrotor-sandbox.webp b/.github/assets/experiences/quadrotor-sandbox.webp new file mode 100644 index 0000000..df1c0be Binary files /dev/null and b/.github/assets/experiences/quadrotor-sandbox.webp differ diff --git a/.github/assets/experiences/reef-rivals.webp b/.github/assets/experiences/reef-rivals.webp new file mode 100644 index 0000000..3169dba Binary files /dev/null and b/.github/assets/experiences/reef-rivals.webp differ diff --git a/.github/assets/experiences/robot-store.webp b/.github/assets/experiences/robot-store.webp new file mode 100644 index 0000000..637bb0d Binary files /dev/null and b/.github/assets/experiences/robot-store.webp differ diff --git a/.github/assets/experiences/room-ritual.webp b/.github/assets/experiences/room-ritual.webp new file mode 100644 index 0000000..ddc06e4 Binary files /dev/null and b/.github/assets/experiences/room-ritual.webp differ diff --git a/.github/assets/experiences/seatline-nyc.webp b/.github/assets/experiences/seatline-nyc.webp new file mode 100644 index 0000000..b73bc6a Binary files /dev/null and b/.github/assets/experiences/seatline-nyc.webp differ diff --git a/.github/assets/experiences/sf-tennis-atlas.webp b/.github/assets/experiences/sf-tennis-atlas.webp new file mode 100644 index 0000000..980281a Binary files /dev/null and b/.github/assets/experiences/sf-tennis-atlas.webp differ diff --git a/.github/assets/experiences/side-one.webp b/.github/assets/experiences/side-one.webp new file mode 100644 index 0000000..db1a219 Binary files /dev/null and b/.github/assets/experiences/side-one.webp differ diff --git a/.github/assets/experiences/sleeping-snorlax.webp b/.github/assets/experiences/sleeping-snorlax.webp new file mode 100644 index 0000000..53b9d67 Binary files /dev/null and b/.github/assets/experiences/sleeping-snorlax.webp differ diff --git a/.github/assets/experiences/sole-archive.webp b/.github/assets/experiences/sole-archive.webp new file mode 100644 index 0000000..a77f717 Binary files /dev/null and b/.github/assets/experiences/sole-archive.webp differ diff --git a/.github/assets/experiences/vanguard-protocol.webp b/.github/assets/experiences/vanguard-protocol.webp new file mode 100644 index 0000000..4294477 Binary files /dev/null and b/.github/assets/experiences/vanguard-protocol.webp differ diff --git a/.github/assets/social-preview.jpg b/.github/assets/social-preview.jpg new file mode 100644 index 0000000..9a3ed1d Binary files /dev/null and b/.github/assets/social-preview.jpg differ diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 0000000..9e33cdd --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,21 @@ +changelog: + exclude: + labels: + - skip-changelog + categories: + - title: New experiences + labels: + - new-experience + - title: Improvements and fixes + labels: + - enhancement + - bug + - title: Documentation + labels: + - documentation + - title: Dependency updates + labels: + - dependencies + - title: Other changes + labels: + - "*" diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml new file mode 100644 index 0000000..ea53604 --- /dev/null +++ b/.github/workflows/validate.yml @@ -0,0 +1,28 @@ +name: Validate catalog + +on: + pull_request: + push: + branches: + - main + +permissions: + contents: read + +concurrency: + group: validate-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + catalog: + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + - name: Validate experience metadata and links + run: node scripts/validate-catalog.mjs + - name: Verify the experience launcher catalog + run: node scripts/play.mjs --list diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..3ade921 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,44 @@ +# Contributor Covenant Code of Conduct + +## Our pledge + +We pledge to make participation in this project a harassment-free experience +for everyone, regardless of age, body size, visible or invisible disability, +ethnicity, sex characteristics, gender identity and expression, level of +experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our standards + +Examples of behavior that contributes to a positive environment include: + +- Demonstrating empathy and kindness toward other people. +- Respecting differing opinions, viewpoints, and experiences. +- Giving and gracefully accepting constructive feedback. +- Accepting responsibility and apologizing to those affected by our mistakes. +- Focusing on what is best for the overall community. + +Examples of unacceptable behavior include harassment, trolling, insulting or +derogatory comments, publishing others' private information, and other conduct +which could reasonably be considered inappropriate in a professional setting. + +## Enforcement + +Project maintainers are responsible for clarifying and enforcing these +standards. They may remove, edit, or reject comments, commits, code, issues, and +other contributions that do not align with this Code of Conduct. + +Report abusive, harassing, or otherwise unacceptable behavior privately through +the contact options at [mint.gg](https://mint.gg). All reports will be reviewed +and investigated promptly and fairly. Maintainers will respect the privacy and +security of reporters. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1. + +[homepage]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..ccb957a --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,69 @@ +# Contributing to Mint Playground + +Thanks for helping make these open-source 3D experiences easier to learn from, +run, and reuse. + +## Good contributions + +The most useful contributions are focused and easy to review: + +- Fix a reproducible bug in one experience. +- Improve keyboard, touch, screen-reader, or reduced-motion support. +- Improve runtime performance or asset-loading behavior with measurements. +- Add tests for an existing system. +- Clarify setup instructions or explain a reusable Three.js pattern. +- Report a broken demo, source link, or catalog entry. + +New experiences are generally published through Mint's release pipeline. Before +building one for this repository, open an experience request so the maintainers +can confirm its scope and publication path. + +## Before you start + +Search existing issues and pull requests first. For a substantial change, open +an issue describing the user problem and proposed approach before investing in +implementation. Issues labeled [`good first issue`](https://github.com/mintdotgg/mint-playground/labels/good%20first%20issue) +are intentionally small and should include enough context to begin. + +Only contribute code and media you have permission to publish. Do not commit +credentials, production data, runtime assets downloaded from Mint CDN, or +third-party media without a compatible license and attribution. The curated +social cards under `.github/assets/experiences` are the intentional exception. + +## Local setup + +This repository uses Node.js 20 or newer and the pnpm version pinned in +`package.json`. + +```bash +corepack enable +pnpm install --filter ./experiences/vanguard-protocol +pnpm play vanguard-protocol +``` + +Replace `vanguard-protocol` with any slug printed by `pnpm play --list`. + +## Validate your change + +Always validate the catalog and build the experience you changed: + +```bash +pnpm validate:catalog +pnpm --filter ./experiences/vanguard-protocol build +``` + +Run experience-specific tests when its `package.json` defines a `test` script. +Use `pnpm build` only when you need to verify the entire workspace. + +## Pull requests + +- Keep a pull request focused on one experience or one repository-wide concern. +- Explain the user-visible result and how you verified it. +- Include before-and-after captures for visual changes. +- Preserve each experience's existing framework and style unless the issue + explicitly calls for a migration. +- Update `experience.json` and the root catalog when metadata or URLs change. +- Do not reformat unrelated files. + +By contributing, you agree that your contribution will be licensed under the +repository's [MIT License](LICENSE). diff --git a/README.md b/README.md index 6df242e..6bcc439 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,92 @@ -# Mint Playground +
+
+
+
+
+ 39 production-grade 3D web experiences, with live demos and source.
+ Explore reusable patterns for browser games, product configurators, data visualization,
+ character animation, physics, audio, and Gaussian splats.
+
+ Play the demos + · + Run one locally + · + Browse by category + · + Contribute +
+ + + +Every app is built with [Mint MCP](https://mcp.mint.gg/) as the 3D asset +pipeline and [Mint Three.js Skills](https://github.com/mintdotgg/mint-threejs-skills) +as the coding workflow. The source is organized as a pnpm workspace, with each +experience isolated in `experiences/
+
+ Character systems+ Vanguard Protocol — animation, selection, cameras, and reactive staging. + |
+
+
+ Game architecture+ Breakline — physics, input, camera rigs, tricks, HUD, and audio. + |
+
+
+ 3D configurators+ Room Ritual — placement, materials, lighting, and persistent state. + |
+
+
+ Gaussian splats+ Impossible Places — streamed worlds and cinematic exploration. + |
+
+
A cozy prehistoric island life game where you gather, fish, catch bugs, plant trees, and help dinosaur neighbors earn your own boulder hut.
diff --git a/experiences/breakline/README.md b/experiences/breakline/README.md
index 1ce28a8..43ab9a3 100644
--- a/experiences/breakline/README.md
+++ b/experiences/breakline/README.md
@@ -1,7 +1,7 @@
# Breakline
-
+
Carve a 1.8 km mountain, chain tricks and grinds, and stay ahead of a Yeti and polar bear in a third-person arcade snowboarding chase.
diff --git a/experiences/cdx-2089-music-player/README.md b/experiences/cdx-2089-music-player/README.md
index 1ce8826..5a32e65 100644
--- a/experiences/cdx-2089-music-player/README.md
+++ b/experiences/cdx-2089-music-player/README.md
@@ -1,7 +1,7 @@
# CDX-2089 Music Player
-
+
A skin-adaptive 3D music player with five futuristic disc decks, audio-reactive visuals, and an unfolding dance floor.
diff --git a/experiences/clicky/README.md b/experiences/clicky/README.md
index 6d1e1e8..50ccb16 100644
--- a/experiences/clicky/README.md
+++ b/experiences/clicky/README.md
@@ -1,7 +1,7 @@
# heyclicky
-
+
Explore a playful desktop AI companion through draggable media windows, switchable 2D and 3D stickers, easter eggs, features, social proof, pricing, and FAQs.
diff --git a/experiences/common-form/README.md b/experiences/common-form/README.md
index bfb5a32..221bc5b 100644
--- a/experiences/common-form/README.md
+++ b/experiences/common-form/README.md
@@ -1,7 +1,7 @@
# Common Form
-
+
Explore six considered everyday objects in a warm interactive showroom, inspect them in 3D, and build a persistent demo cart.
diff --git a/experiences/complete-shelf/README.md b/experiences/complete-shelf/README.md
index ee8b7c6..332d8c6 100644
--- a/experiences/complete-shelf/README.md
+++ b/experiences/complete-shelf/README.md
@@ -1,7 +1,7 @@
# The Complete Shelf
-
+
Browse nineteen procedural hardcovers on a continuous 3D shelf, then pull any volume forward to orbit, zoom, and inspect its editorial details.
diff --git a/experiences/compound-visualization/README.md b/experiences/compound-visualization/README.md
index e9b7a9b..603d161 100644
--- a/experiences/compound-visualization/README.md
+++ b/experiences/compound-visualization/README.md
@@ -1,7 +1,7 @@
# Compound Visualization
-
+
Explore more than 40 molecules and crystals, switch rendering styles, inspect atoms and bonds, measure geometry, and search PubChem.
diff --git a/experiences/dead-reckoning/README.md b/experiences/dead-reckoning/README.md
index 718006d..f1ce6ad 100644
--- a/experiences/dead-reckoning/README.md
+++ b/experiences/dead-reckoning/README.md
@@ -1,7 +1,7 @@
# Dead Reckoning
-
+
Play a complete 3D chess match where fog reveals only squares your pieces attack while a greedy AI moves unseen.
diff --git a/experiences/dynasty-index/README.md b/experiences/dynasty-index/README.md
index 916037b..aafd56e 100644
--- a/experiences/dynasty-index/README.md
+++ b/experiences/dynasty-index/README.md
@@ -1,7 +1,7 @@
# Dynasty Index
-
+
An immersive collector vault for selecting and inspecting six fictional premium sports trading cards.
diff --git a/experiences/elemental-chess/README.md b/experiences/elemental-chess/README.md
index 4afdee1..ca3e30e 100644
--- a/experiences/elemental-chess/README.md
+++ b/experiences/elemental-chess/README.md
@@ -1,7 +1,7 @@
# Elemental Chess
-
+
Choose Water, Earth, Fire, or Air, face a rival, and play a complete cinematic 3D chess match in a Mint-authored elemental world.
diff --git a/experiences/fieldline-santa-clara/README.md b/experiences/fieldline-santa-clara/README.md
index eb82cbd..d1c11be 100644
--- a/experiences/fieldline-santa-clara/README.md
+++ b/experiences/fieldline-santa-clara/README.md
@@ -1,7 +1,7 @@
# Fieldline Santa Clara
-
+
Explore every section at Levi’s Stadium, enter modeled seated views, compare sightlines, and inspect obstruction, shade, weather, and event context before opening official tickets.
diff --git a/experiences/final-whistle/README.md b/experiences/final-whistle/README.md
index 073f3a8..a4274bd 100644
--- a/experiences/final-whistle/README.md
+++ b/experiences/final-whistle/README.md
@@ -1,7 +1,7 @@
# Final Whistle: Spain vs Argentina
-
+
Lead Spain through a 150-second 3D arcade football final against Argentina with passing, tackles, charged shots, and golden-goal drama.
diff --git a/experiences/financial-city/README.md b/experiences/financial-city/README.md
index 447cd3d..fff2327 100644
--- a/experiences/financial-city/README.md
+++ b/experiences/financial-city/README.md
@@ -1,7 +1,7 @@
# Financial City
-
+
Shape a private monthly budget and watch traffic, civic services, construction pressure, and forty city blocks respond in a living miniature metropolis.
diff --git a/experiences/flappy-bird-3d/README.md b/experiences/flappy-bird-3d/README.md
index 05c4629..39d605c 100644
--- a/experiences/flappy-bird-3d/README.md
+++ b/experiences/flappy-bird-3d/README.md
@@ -1,7 +1,7 @@
# Flappy Bird 3D
-
+
Fly a selectable 3D bird down an endless ocean causeway, flapping through randomized pipe gaps for a high score.
diff --git a/experiences/forgotten-experiment/README.md b/experiences/forgotten-experiment/README.md
index e7acbc2..44035ef 100644
--- a/experiences/forgotten-experiment/README.md
+++ b/experiences/forgotten-experiment/README.md
@@ -1,7 +1,7 @@
# The Forgotten Experiment
-
+
Search two abandoned laboratory rooms, collect and equip tools, solve layered evidence puzzles, and unlock Dr. Elias Vale's experiment.
diff --git a/experiences/four-fields/README.md b/experiences/four-fields/README.md
index 1fd6b50..990a510 100644
--- a/experiences/four-fields/README.md
+++ b/experiences/four-fields/README.md
@@ -1,7 +1,7 @@
# Four Fields
-
+
Harvest corn, potatoes, tomatoes, and strawberries across four timed plots in a warm storybook miniature farm.
diff --git a/experiences/helios-form/README.md b/experiences/helios-form/README.md
index 3d64a5f..55788d3 100644
--- a/experiences/helios-form/README.md
+++ b/experiences/helios-form/README.md
@@ -1,7 +1,7 @@
# Helios Form
-
+
A luminous orbital-vehicle showroom for inspecting six futuristic craft and their onboard systems.
diff --git a/experiences/impossible-places/README.md b/experiences/impossible-places/README.md
index 0a28aad..9f35f66 100644
--- a/experiences/impossible-places/README.md
+++ b/experiences/impossible-places/README.md
@@ -1,7 +1,7 @@
# Impossible Places
-
+
A cinematic travel magazine spanning five explorable Mint-generated Gaussian-splat worlds.
diff --git a/experiences/jimothys-backyard-dash/README.md b/experiences/jimothys-backyard-dash/README.md
index 32158f3..ad22193 100644
--- a/experiences/jimothys-backyard-dash/README.md
+++ b/experiences/jimothys-backyard-dash/README.md
@@ -1,7 +1,7 @@
# Jimothy’s Backyard Dash
-
+
Guide Jimothy through a fast three-lane backyard race, dodging obstacles, collecting Moon Tokens, and battling two rivals across five streamed worlds.
diff --git a/experiences/kirby-smash/README.md b/experiences/kirby-smash/README.md
index 48df5e3..fbc844a 100644
--- a/experiences/kirby-smash/README.md
+++ b/experiences/kirby-smash/README.md
@@ -1,7 +1,7 @@
# Kirby Smash
-
+
Battle across three floating arenas in a 2.5D platform fighter with four selectable combatants, local two-player or CPU matches, percent damage, stocks, and Smash-style knockback.
diff --git a/experiences/memory-constellation/README.md b/experiences/memory-constellation/README.md
index da569f8..f47be0e 100644
--- a/experiences/memory-constellation/README.md
+++ b/experiences/memory-constellation/README.md
@@ -1,7 +1,7 @@
# Memory Constellation
-
+
Capture private memories locally, explore their connections as a 3D constellation, and revisit every moment in an accessible timeline.
diff --git a/experiences/mint-hoops/README.md b/experiences/mint-hoops/README.md
index eacfffd..acd2ff2 100644
--- a/experiences/mint-hoops/README.md
+++ b/experiences/mint-hoops/README.md
@@ -1,7 +1,7 @@
# Mint Hoops
-
+
A 60-second arcade basketball score attack with drag-to-shoot controls, combo multipliers, and a three-point finale.
diff --git a/experiences/mint-putt/README.md b/experiences/mint-putt/README.md
index c3b798e..6d36b0e 100644
--- a/experiences/mint-putt/README.md
+++ b/experiences/mint-putt/README.md
@@ -1,7 +1,7 @@
# Genesis Grove Putt-Putt
-
+
An 18-hole putting round through California-inspired Gaussian splat worlds, with drag-to-aim shots, touch controls, and a running scorecard.
diff --git a/experiences/mossbound/README.md b/experiences/mossbound/README.md
index 2d462bf..049b0f9 100644
--- a/experiences/mossbound/README.md
+++ b/experiences/mossbound/README.md
@@ -1,7 +1,7 @@
# Mossbound
-
+
A first-person fantasy melee run through five escalating waves of corrupted grove creatures, ending in a showdown with the Thornheart.
diff --git a/experiences/neon-relic/README.md b/experiences/neon-relic/README.md
index 4b8be56..3e4aa50 100644
--- a/experiences/neon-relic/README.md
+++ b/experiences/neon-relic/README.md
@@ -1,7 +1,7 @@
# Neon Relic
-
+
An interactive poster gallery where four sci-fi relics reshape the typography, lighting, materials, and sound.
diff --git a/experiences/orbit-week/README.md b/experiences/orbit-week/README.md
index 9fe1561..58d7f40 100644
--- a/experiences/orbit-week/README.md
+++ b/experiences/orbit-week/README.md
@@ -1,7 +1,7 @@
# Orbit Week
-
+
A cinematic 3D weekly planner where each day is a planet and every task becomes a mission you can add, complete, and carry through the week.
diff --git a/experiences/periodic-table/README.md b/experiences/periodic-table/README.md
index b9f3fb7..c38d9f2 100644
--- a/experiences/periodic-table/README.md
+++ b/experiences/periodic-table/README.md
@@ -1,7 +1,7 @@
# Periodic Table 3D
-
+
Browse all 118 elements on a tactile 3D table, open any tile, and orbit an animated atom surrounded by detailed scientific properties.
diff --git a/experiences/pogo-man/README.md b/experiences/pogo-man/README.md
index 99267b5..9a05351 100644
--- a/experiences/pogo-man/README.md
+++ b/experiences/pogo-man/README.md
@@ -1,7 +1,7 @@
# Pogo Man
-
+
Charge double jumps to clear city traffic, build combos, and chase a high score in a one-button 3D arcade run.
diff --git a/experiences/project-forge-5/README.md b/experiences/project-forge-5/README.md
index d1f2b6e..cc6974f 100644
--- a/experiences/project-forge-5/README.md
+++ b/experiences/project-forge-5/README.md
@@ -1,7 +1,7 @@
# Project Forge-5
-
+
Command five specialized robots through indoor qualification missions and an outdoor physics sandbox.
diff --git a/experiences/quadrotor-sandbox/README.md b/experiences/quadrotor-sandbox/README.md
index 23d1e92..187e743 100644
--- a/experiences/quadrotor-sandbox/README.md
+++ b/experiences/quadrotor-sandbox/README.md
@@ -1,7 +1,7 @@
# Quadrotor Sandbox
-
+
Pilot seven distinct aircraft through a collider-rich city with real flight models, wind, sensing, tuning, landings, and launch staging.
diff --git a/experiences/reef-rivals/README.md b/experiences/reef-rivals/README.md
index 605f2c4..bc25fe9 100644
--- a/experiences/reef-rivals/README.md
+++ b/experiences/reef-rivals/README.md
@@ -1,7 +1,7 @@
# Reef Rivals: Treasure Tide
-
+
A three-round tropical projectile battler where tube-riding heroes aim, charge, and outshoot an escalating pirate raft crew.
diff --git a/experiences/robot-store/README.md b/experiences/robot-store/README.md
index 59b2c1d..4ed7a36 100644
--- a/experiences/robot-store/README.md
+++ b/experiences/robot-store/README.md
@@ -1,7 +1,7 @@
# Future Form: Robot Store
-
+
Explore an editorial 3D archive of 15 humanoid robots, orbit each digital twin, and compare their capabilities.
diff --git a/experiences/room-ritual/README.md b/experiences/room-ritual/README.md
index d936118..fb55d29 100644
--- a/experiences/room-ritual/README.md
+++ b/experiences/room-ritual/README.md
@@ -1,7 +1,7 @@
# Room Ritual
-
+
Arrange six statement furniture pieces in a soft-brutalist loft, customize finishes and lighting, and save, compare, or cart room plans.
diff --git a/experiences/seatline-nyc/README.md b/experiences/seatline-nyc/README.md
index 1cc1357..780ff68 100644
--- a/experiences/seatline-nyc/README.md
+++ b/experiences/seatline-nyc/README.md
@@ -1,7 +1,7 @@
# Seatline NYC
-
+
Choose a New York theater, showtime, and seat, then preview the 3D screen angle and sightline before opening the venue’s live listing.
diff --git a/experiences/sf-tennis-atlas/README.md b/experiences/sf-tennis-atlas/README.md
index 673b0bb..1d42e05 100644
--- a/experiences/sf-tennis-atlas/README.md
+++ b/experiences/sf-tennis-atlas/README.md
@@ -1,7 +1,7 @@
# SF Tennis Atlas
-
+
A source-aware 3D atlas for exploring all 64 official San Francisco public tennis facilities.
diff --git a/experiences/side-one/README.md b/experiences/side-one/README.md
index af853c5..b59552f 100644
--- a/experiences/side-one/README.md
+++ b/experiences/side-one/README.md
@@ -1,7 +1,7 @@
# Side One
-
+
Browse seven landmark releases across nine pressings, inspect every sleeve and track list, and cue a record on a configurable 3D turntable.
diff --git a/experiences/sleeping-snorlax/README.md b/experiences/sleeping-snorlax/README.md
index efcde60..384506f 100644
--- a/experiences/sleeping-snorlax/README.md
+++ b/experiences/sleeping-snorlax/README.md
@@ -1,7 +1,7 @@
# Sleeping Snorlax
-
+
Poke a peacefully sleeping Snorlax to trigger escalating procedural reactions, combos, wake-ups, and permanent growth.
diff --git a/experiences/sole-archive/README.md b/experiences/sole-archive/README.md
index 69269f1..0224fbb 100644
--- a/experiences/sole-archive/README.md
+++ b/experiences/sole-archive/README.md
@@ -1,7 +1,7 @@
# Sole Archive
-
+
Inspect and configure six fictional sneakers in an underground boutique with exploded materials, comparison, wishlist, sizing, and a persistent demo cart.
diff --git a/experiences/vanguard-protocol/README.md b/experiences/vanguard-protocol/README.md
index 92927f8..ed051b6 100644
--- a/experiences/vanguard-protocol/README.md
+++ b/experiences/vanguard-protocol/README.md
@@ -1,7 +1,7 @@
# Vanguard Protocol
-
+
A cinematic five-character combat roster with animated fighters and reactive tactical staging.
diff --git a/package.json b/package.json
index 7793574..5b308e8 100644
--- a/package.json
+++ b/package.json
@@ -5,6 +5,8 @@
"license": "MIT",
"packageManager": "pnpm@10.6.2",
"scripts": {
- "build": "pnpm -r --if-present build"
+ "build": "pnpm -r --if-present build",
+ "play": "node scripts/play.mjs",
+ "validate:catalog": "node scripts/validate-catalog.mjs"
}
}
diff --git a/scripts/play.mjs b/scripts/play.mjs
new file mode 100644
index 0000000..8189d7b
--- /dev/null
+++ b/scripts/play.mjs
@@ -0,0 +1,99 @@
+import { readdir, readFile } from "node:fs/promises";
+import { spawn } from "node:child_process";
+import { createInterface } from "node:readline/promises";
+import { stdin, stdout } from "node:process";
+
+const experiencesDirectory = new URL("../experiences/", import.meta.url);
+
+async function getExperiences() {
+ const entries = await readdir(experiencesDirectory, { withFileTypes: true });
+ const experiences = [];
+
+ for (const entry of entries) {
+ if (!entry.isDirectory()) continue;
+
+ try {
+ const manifest = JSON.parse(
+ await readFile(new URL(`${entry.name}/experience.json`, experiencesDirectory)),
+ );
+ const packageJson = JSON.parse(
+ await readFile(new URL(`${entry.name}/package.json`, experiencesDirectory)),
+ );
+
+ if (packageJson.scripts?.dev) {
+ experiences.push({
+ order: manifest.order ?? Number.MAX_SAFE_INTEGER,
+ slug: entry.name,
+ title: manifest.title ?? entry.name,
+ });
+ }
+ } catch {
+ // Catalog validation reports incomplete experience directories in detail.
+ }
+ }
+
+ return experiences.sort((left, right) =>
+ left.order - right.order || left.title.localeCompare(right.title),
+ );
+}
+
+function printExperiences(experiences) {
+ const width = Math.max(...experiences.map(({ slug }) => slug.length));
+ for (const { slug, title } of experiences) {
+ console.log(`${slug.padEnd(width)} ${title}`);
+ }
+}
+
+async function chooseExperience(experiences) {
+ console.log("Choose an experience:\n");
+ experiences.forEach(({ title }, index) => {
+ console.log(`${String(index + 1).padStart(2)}. ${title}`);
+ });
+
+ const prompt = createInterface({ input: stdin, output: stdout });
+ const answer = await prompt.question("\nNumber or slug: ");
+ prompt.close();
+
+ const number = Number.parseInt(answer, 10);
+ if (String(number) === answer.trim() && number >= 1 && number <= experiences.length) {
+ return experiences[number - 1];
+ }
+
+ return experiences.find(({ slug }) => slug === answer.trim());
+}
+
+const experiences = await getExperiences();
+const argument = process.argv[2];
+
+if (argument === "--list") {
+ printExperiences(experiences);
+ process.exit(0);
+}
+
+const selected = argument
+ ? experiences.find(({ slug }) => slug === argument)
+ : await chooseExperience(experiences);
+
+if (!selected) {
+ console.error(`Unknown experience: ${argument ?? "selection"}\n`);
+ console.error("Run `pnpm play --list` to see every available slug.");
+ process.exit(1);
+}
+
+console.log(`\nStarting ${selected.title}…\n`);
+
+const child = spawn(
+ "pnpm",
+ ["--dir", `experiences/${selected.slug}`, "dev"],
+ { cwd: new URL("..", import.meta.url), stdio: "inherit" },
+);
+
+child.on("error", (error) => {
+ console.error(`Unable to start pnpm: ${error.message}`);
+ process.exit(1);
+});
+
+child.on("exit", (code, signal) => {
+ if (signal) process.kill(process.pid, signal);
+ process.exit(code ?? 1);
+});
diff --git a/scripts/validate-catalog.mjs b/scripts/validate-catalog.mjs
new file mode 100644
index 0000000..450b411
--- /dev/null
+++ b/scripts/validate-catalog.mjs
@@ -0,0 +1,103 @@
+import { readdir, readFile } from "node:fs/promises";
+
+const repositoryRoot = new URL("../", import.meta.url);
+const experiencesDirectory = new URL("experiences/", repositoryRoot);
+const readme = await readFile(new URL("README.md", repositoryRoot), "utf8");
+const entries = await readdir(experiencesDirectory, { withFileTypes: true });
+const directories = entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
+const errors = [];
+const records = [];
+
+async function readJson(path, label) {
+ try {
+ return JSON.parse(await readFile(new URL(path, repositoryRoot), "utf8"));
+ } catch (error) {
+ errors.push(`${label}: ${error.message}`);
+ return null;
+ }
+}
+
+for (const directory of directories) {
+ const prefix = `experiences/${directory}`;
+ const cardPath = `.github/assets/experiences/${directory}.webp`;
+ const manifest = await readJson(`${prefix}/experience.json`, `${directory}/experience.json`);
+ const packageJson = await readJson(`${prefix}/package.json`, `${directory}/package.json`);
+ let experienceReadme = "";
+
+ try {
+ experienceReadme = await readFile(new URL(`${prefix}/README.md`, repositoryRoot), "utf8");
+ } catch {
+ errors.push(`${directory}: missing README.md`);
+ }
+
+ try {
+ const card = await readFile(new URL(cardPath, repositoryRoot));
+ const isWebp =
+ card.subarray(0, 4).toString("ascii") === "RIFF" &&
+ card.subarray(8, 12).toString("ascii") === "WEBP";
+ if (!isWebp) errors.push(`${directory}: social card is not a valid WebP file`);
+ } catch {
+ errors.push(`${directory}: missing local social card`);
+ }
+
+ if (!manifest || !packageJson) continue;
+
+ if (manifest.slug !== directory) {
+ errors.push(`${directory}: manifest slug is ${JSON.stringify(manifest.slug)}`);
+ }
+ if (!Number.isFinite(manifest.order)) {
+ errors.push(`${directory}: order must be a number`);
+ }
+ if (!manifest.title?.trim()) {
+ errors.push(`${directory}: title is required`);
+ }
+ if (!manifest.description?.trim()) {
+ errors.push(`${directory}: description is required`);
+ }
+ if (!Array.isArray(manifest.tags) || manifest.tags.length === 0) {
+ errors.push(`${directory}: at least one tag is required`);
+ }
+ if (!packageJson.scripts?.dev) {
+ errors.push(`${directory}: package.json must define a dev script`);
+ }
+ if (!packageJson.scripts?.build) {
+ errors.push(`${directory}: package.json must define a build script`);
+ }
+ if (!readme.includes(`experiences/${directory}`)) {
+ errors.push(`${directory}: root README is missing a source link`);
+ }
+ if (!readme.includes(`https://play.mint.gg/${directory}`)) {
+ errors.push(`${directory}: root README is missing a live demo link`);
+ }
+ if (!readme.includes(cardPath)) {
+ errors.push(`${directory}: root README is missing its local social card`);
+ }
+ if (!experienceReadme.includes(`../../${cardPath}`)) {
+ errors.push(`${directory}: experience README is missing its local social card`);
+ }
+
+ records.push({ directory, order: manifest.order, title: manifest.title });
+}
+
+for (const field of ["title"]) {
+ const grouped = new Map();
+ for (const record of records) {
+ const value = record[field];
+ grouped.set(value, [...(grouped.get(value) ?? []), record]);
+ }
+ for (const [value, matches] of grouped.entries()) {
+ if (matches.length > 1) {
+ errors.push(
+ `duplicate ${field} ${JSON.stringify(value)}: ${matches.map(({ directory }) => directory).join(", ")}`,
+ );
+ }
+ }
+}
+
+if (errors.length > 0) {
+ console.error(`Catalog validation failed with ${errors.length} error${errors.length === 1 ? "" : "s"}:\n`);
+ errors.forEach((error) => console.error(`- ${error}`));
+ process.exit(1);
+}
+
+console.log(`Catalog valid: ${records.length} experiences.`);