Open-source project
EvanBacon/pillar-valley avatar
EvanBacon/pillar-valley

Pillar Valley: a three.js game shipped through Expo and the app stores

👾A cross-platform video game built with Expo and three.js

772 stars81 forksTypeScriptMIT

At a glance

What is it?
Pillar Valley is a cross-platform video game built with Expo, three.js, GSAP and React Native, published as a web build, an iOS app, an Android app and an App Clip. It is a finished product, so the repository is worth reading as a deployment template rather than as a game engine.
Who is it for?
Pillar Valley is worth studying if you want to ship a real three.js game through one Expo codebase to the web, both app stores and an App Clip, since the release scripts, eas.json and store configuration are all in the tree. It is a poor starting point for a new game, because the code is organised around an existing product with its own assets and screens.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 150 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What Pillar Valley ships and where it runs

The repository is the source of a finished game rather than a library or a tutorial. The README calls it a cross-platform video game built with Expo and three.js, and the Play section links to a web build at pillarvalley.expo.app, the Apple App Store listing, the Google Play Store listing and an App Clip URL.

The App Clip entry is the unusual part. An App Clip is a lightweight iOS payload that opens straight into an experience without a full install, and shipping one requires the bundle identifier, the app clip code generator and a hosted target, all of which are present in the tree. The app-clip-code script in package.json passes a URL, a foreground colour of 000000, a background colour of EE7733 and an output path for the generated SVG, and the repository keeps that generated file as app-clip.svg.

The platform spread is broad. The topics list names android, ios, react-native, react-native-web, expo, threejs, gsap, rematch, react-navigation and tutorial, so the same codebase covers native and web targets, with Rematch for state and react-navigation for navigation. The published store identifiers follow the com.evanbacon.pillarvalley pattern, and the iOS listing uses the identifier com.evanbacon.pillarvalley.clip for the clip.

The three layers: Expo, three.js and GSAP

The README lists the stack in three names, and each one owns a different layer of the game.

Expo is the runtime and distribution layer. The package.json points the entry point at expo-router/entry, so routing is file based rather than a navigation library you configure by hand, and the Expo SDK is pinned at ^55.0.13 with the router at ~55.0.11. The rest of the Expo surface is wide: expo-audio, expo-haptics, expo-sensors, expo-sqlite, expo-blur, expo-glass-effect, expo-font, expo-splash-screen, expo-store-review, expo-quick-actions and expo-device. Together they cover sound, physical feedback, motion sensors, local persistence, visual effects and the store review prompt.

three.js is the renderer, doing the actual 3D work in a WebGL context. GSAP is the animation layer, and the topics list tween and gsap separately, which suggests the game leans on scripted tweens for motion rather than on frame-by-frame physics.

The native modules live in a modules/ directory, and there is a store/ directory, a targets/ directory and a scripts/ directory at the top level. app.config.ts and app.json hold the Expo configuration, eas.json holds the EAS build profile, and store.config.json holds the store listing metadata.

Running the project locally and the native build path

The scripts section of package.json is the practical entry point for anyone cloning the repository. The start script launches the Expo dev client, and there are separate scripts for the two native targets.

json
    "start": "expo start --dev-client",
    "android": "expo run:android",
    "ios": "expo run:ios",

The dev client flag matters, because a bare Expo start would open the standard development runtime rather than the custom client this project ships, and the app depends on native modules that are not in the standard runtime.

The web build has its own two-step path, with a prepublish step that exports the web bundle and a publish step that deploys it to production.

json
    "prepublish:web": "expo export -p web",
    "publish:web": "eas deploy --prod",

The entry point sits at the top of the same file.

json
  "main": "expo-router/entry",

That matters more than it looks. With expo-router as the entry, the src/ directory holds route files rather than a single App component, which is the structural difference between a router-based Expo app and a classic one. The src/ directory also contains a constants/Licenses.json file, which the licenses script fills.

Quality gates, patched dependencies and licence tracking

Three details in the scripts show how the project is kept honest.

Testing runs through jest with the jest-expo preset, invoked through the local binary rather than a global one.

json
    "test": "node_modules/.bin/jest",

Linting covers the src directory and respects the gitignore, and there is a fix variant, plus a prettier pass that only targets root JavaScript files.

json
    "lint": "eslint --ignore-path .gitignore ./src",

The most telling script is postinstall, which runs patch-package. That means the project carries patches applied on top of its dependencies after install, which is how a project keeps a fix to a library it does not own. Anyone reading the dependency versions should also read the patches directory, because the effective behaviour is not the published version alone.

The licenses script crawls npm for the licence text of direct dependencies and writes the result into src/constants/Licenses.json, which is how an app that ships to both stores keeps the third-party licence list it has to display. There is also a metadata script that pushes configuration to EAS metadata, and store/ holds the store listing assets, with custom-screenshots/ and screenshots/ directories at the top level for the images each store requires.

Where Pillar Valley is the wrong template

The first limitation is that it is a game with content, not a generic engine. The repository carries store assets, promotional images, GoogleService-Info.plist, google-services.json, an App Clip generator configuration and a set of screenshots, all of which are specific to one product. The reusable part is the plumbing, not the game.

The second is the pinned toolchain. The dependency set is large and specific, with most Expo packages at ~55.0.x, React types at ~19.2.10 and several third-party packages at caret ranges. Adopting this project as a starting point means accepting that dependency set and the patch-package fixes that go with it, or spending time unpicking it.

The third is the patching. patch-package in postinstall is a pragmatic choice and a maintenance liability at the same time: the patches are tied to exact dependency versions, and a dependency bump can silently invalidate them, producing behaviour that differs from what the game was tested against.

The fourth is the age of the code. The last push to the repository was on 2026-05-26, and the default branch is master, which is not the usual default for a new Expo project. Anyone planning to build on it should expect to update the SDK and the native project files before adding features.

Licence, distribution and the cost of shipping a game this way

The project is under the MIT licence, with the LICENSE file at the root of the repository, and the author is listed as Evan Bacon in package.json, where the package is marked private. A private package means it is not published to npm, which is normal for an application repository and also means the game source is distributed through the app stores and the web build rather than as a package.

Shipping through EAS carries a specific cost. The publish:web script deploys to production through eas deploy --prod, and the store builds implied by the android and ios scripts require the native project files in the _android/ directory and the Apple configuration in the store/ directory, plus signing credentials held in the EAS project rather than in the repository. A team without an Apple Developer account or without EAS configuration cannot produce a signed build from this tree.

The legal side of distribution is handled in the repository. PRIVACY.md and TERMS.md are present, and the licenses script generates the third-party licence list into src/constants/Licenses.json, which is the file an app store submission usually asks for. The GoogleService-Info.plist and google-services.json files are the Firebase configuration for the iOS and Android builds, and shipping with those in the tree is normal for a client app but worth reviewing before you fork the project.

Editorial conclusion

Pillar Valley is worth studying if you want to ship a real three.js game through one Expo codebase to the web, both app stores and an App Clip, since the release scripts, eas.json and store configuration are all in the tree. It is a poor starting point for a new game, because the code is organised around an existing product with its own assets and screens. Verify first that the Expo SDK version in package.json, at 55.x, matches the toolchain you plan to use, since the whole project is pinned to that line.

Frequently asked questions

How do I run the Pillar Valley game locally?

The package.json start script runs expo start --dev-client, and separate scripts exist for expo run:android and expo run:ios. A postinstall step runs npx patch-package, so dependency patches are applied after install.

Where can I play Pillar Valley?

The README links to a web build at pillarvalley.expo.app, the Apple App Store listing, the Google Play Store listing, and an App Clip URL at appclip.apple.com. The game is built with Expo, three.js and GSAP and is described as a cross-platform video game.

What does the Expo App Clip support in Pillar Valley do?

The repository ships an app-clip-code script that generates the App Clip payload from a URL with a foreground colour of 000000 and a background colour of EE7733, writing the result to app-clip.svg, and the README links to the live App Clip URL for the com.evanbacon.pillarvalley.clip identifier.

How does Pillar Valley publish its web build?

Two scripts do the work. prepublish:web runs expo export -p web to produce the web bundle, and publish:web runs eas deploy --prod to deploy it to production, with a metadata script that pushes configuration through eas metadata:push.

Official sources

  1. EvanBacon/pillar-valley on GitHub
  2. Issues
  3. License: MIT
  4. Project website
  5. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/evanbacon-pillar-valley.svg)](https://hysenlabs.com/projects/evanbacon-pillar-valley)

Was this useful?