An endless flight with the Coyotiv mascot.
One TypeScript codebase. Runs in the browser, and compiles ahead of time to native macOS and Xbox apps with no JavaScript engine.
Fly over a ring-shaped world that is generated as you go. Collect yellow stars to keep your energy up, dodge red bombs, and see how far you get. Each level is one full day: you take off at dawn and fly through noon, dusk and a starlit night before the sun comes up again.
Skytail builds on Karim Maaloul's 2016 Codrops tutorial The Aviator. It keeps the original game's idea and rebuilds everything else: a deterministic world generator, a mascot that moves on spring physics, a day-night sky, a reactive sound mix, and a native build pipeline.
![]() | ![]() |
| Desert at noon | Midnight. The stars come out and the balloon burners glow. |
![]() | ![]() |
| An autumn coast on level 2 | Winter on level 3, with a suspension bridge on the right |
Your energy drains the whole time you fly. Each star gives back 3 points and each bomb you hit takes 10. When your energy runs out, the game ends.
The game, world, HUD and render loop live in src/ and are shared by
every platform. Each platform only adds a thin entry point.
flowchart LR
game["Game code in src/<br/>TypeScript + three.js"]
game --> web["web/main.ts<br/>Vite build"] --> browser["Browser<br/>WebGL"]
game --> mac["index.tsx<br/>gea build --target macos"] --> macos["macOS app<br/>C++ · ANGLE · Metal"]
game --> xbox["xbox/index.ts<br/>npm run build:xbox"] --> console["Xbox app<br/>C++ · UWP · ANGLE"]
src/native-app.ts creates a native WebGL 2 context and
passes it to the unmodified three.js WebGLRenderer. GeaStack's
@geastack/native-webgl-angle
host provides the context through ANGLE, which runs on Metal on macOS.src/app.tsx builds the macOS view tree
with <NSView> elements from @geastack/apple.AVAudioSourceNode on macOS and XAudio2 on Xbox.xbox/three_uwp_main.cpp is
a C++/CX app that renders at a fixed 3840×2160. It reads the gamepad through
Windows.Gaming.Input.src/worldgen/ builds the world one cell at a time, just before
it comes into view.
You need Node.js 22.15 or later.
git clone https://github.com/coyotiv/skytail.git
cd skytail
npm ci
npm --prefix web ci
npm run dev
Open the local URL that Vite prints.
The browser version lives in web/ and has its own package.json. That
is why there are two install commands.
To make a production build in web/dist/ and preview it:
npm run build
npm run preview
You need the Xcode Command Line Tools (xcode-select --install). Run this from
the repository root:
npx gea build --target macos
open dist/macos/skytail/Skytail.app
gea is the GeaStack CLI. It compiles the game, three.js included, to C++ and
builds a native app. The first build can take 15 minutes or more.
Xbox builds run on a remote Windows build host and are installed on an Xbox in Developer Mode. The local machine needs bash, SSH, SCP and tar. The Windows build host needs:
libEGL.dll, libGLESv2.dll and d3dcompiler_47.dllBuild with your own host and directories:
npm run build:xbox -- --host build-host --remote-dir C:/skytail-build --angle-dir C:/uwp-angle
--host is the SSH host of the Windows build machine.--remote-dir is the build directory on that host. Use an absolute path with
forward slashes and no spaces.--angle-dir is the directory on that host that contains the UWP ANGLE DLLs.You can also set GEA_XBOX_BUILD_HOST, GEA_XBOX_BUILD_DIR and
GEA_XBOX_ANGLE_DIR instead of passing the flags.
To only generate and stage the build inputs locally, without the remote build:
npm run build:xbox -- --stage-only
To install and start the game on your Xbox, set GEA_XBOX_AUTH to your Device
Portal credentials as username:password, then run:
npm run deploy:xbox -- --xbox xbox-address
You can set GEA_XBOX_HOST instead of passing --xbox.
| Command | What it does |
|---|---|
npm run dev | Starts the browser version with live reload |
npm test | Runs the unit tests with Vitest |
npm run check | Type-checks the game |
npm run check:native | Type-checks the code used by the native builds |
npm --prefix web run check | Type-checks the browser entry point |
The code is in src/:
| Directory | Contents |
|---|---|
game/ | Game loop, energy, levels, and star and bomb waves |
actors/ | The flying mascot, stars, bombs and particles |
world/ | Terrain ring, sky, lighting, wind and the day-night cycle |
worldgen/ | Procedural generation of terrain, roads, rivers and scenery |
hud/ | Title screen, distance, energy and level display |
io/ | Input and audio |
kit/ | The 121 embedded low-poly models |
config.ts | Gameplay tuning values such as speed, distances and pools |
The browser entry point is web/main.ts. The native entry point
is index.tsx. The Xbox host code is in xbox/.
Skytail builds on The Aviator by Karim Maaloul, published with Codrops in 2016.
The models and favicon come from Coyotiv. The audio was generated with ElevenLabs. The native builds are made with GeaStack.
The code is MIT licensed. Third-party source credits and license terms are in LICENSE.third-party. The Inter and Unbounded font licenses are in LICENSE.fonts.
An endless flight with the Coyotiv mascot.
One TypeScript codebase. Runs in the browser, and compiles ahead of time to native macOS and Xbox apps with no JavaScript engine.
Fly over a ring-shaped world that is generated as you go. Collect yellow stars to keep your energy up, dodge red bombs, and see how far you get. Each level is one full day: you take off at dawn and fly through noon, dusk and a starlit night before the sun comes up again.
Skytail builds on Karim Maaloul's 2016 Codrops tutorial The Aviator. It keeps the original game's idea and rebuilds everything else: a deterministic world generator, a mascot that moves on spring physics, a day-night sky, a reactive sound mix, and a native build pipeline.
![]() | ![]() |
| Desert at noon | Midnight. The stars come out and the balloon burners glow. |
![]() | ![]() |
| An autumn coast on level 2 | Winter on level 3, with a suspension bridge on the right |
Your energy drains the whole time you fly. Each star gives back 3 points and each bomb you hit takes 10. When your energy runs out, the game ends.
The game, world, HUD and render loop live in src/ and are shared by
every platform. Each platform only adds a thin entry point.
flowchart LR
game["Game code in src/<br/>TypeScript + three.js"]
game --> web["web/main.ts<br/>Vite build"] --> browser["Browser<br/>WebGL"]
game --> mac["index.tsx<br/>gea build --target macos"] --> macos["macOS app<br/>C++ · ANGLE · Metal"]
game --> xbox["xbox/index.ts<br/>npm run build:xbox"] --> console["Xbox app<br/>C++ · UWP · ANGLE"]
src/native-app.ts creates a native WebGL 2 context and
passes it to the unmodified three.js WebGLRenderer. GeaStack's
@geastack/native-webgl-angle
host provides the context through ANGLE, which runs on Metal on macOS.src/app.tsx builds the macOS view tree
with <NSView> elements from @geastack/apple.AVAudioSourceNode on macOS and XAudio2 on Xbox.xbox/three_uwp_main.cpp is
a C++/CX app that renders at a fixed 3840×2160. It reads the gamepad through
Windows.Gaming.Input.src/worldgen/ builds the world one cell at a time, just before
it comes into view.
You need Node.js 22.15 or later.
git clone https://github.com/coyotiv/skytail.git
cd skytail
npm ci
npm --prefix web ci
npm run dev
Open the local URL that Vite prints.
The browser version lives in web/ and has its own package.json. That
is why there are two install commands.
To make a production build in web/dist/ and preview it:
npm run build
npm run preview
You need the Xcode Command Line Tools (xcode-select --install). Run this from
the repository root:
npx gea build --target macos
open dist/macos/skytail/Skytail.app
gea is the GeaStack CLI. It compiles the game, three.js included, to C++ and
builds a native app. The first build can take 15 minutes or more.
Xbox builds run on a remote Windows build host and are installed on an Xbox in Developer Mode. The local machine needs bash, SSH, SCP and tar. The Windows build host needs:
libEGL.dll, libGLESv2.dll and d3dcompiler_47.dllBuild with your own host and directories:
npm run build:xbox -- --host build-host --remote-dir C:/skytail-build --angle-dir C:/uwp-angle
--host is the SSH host of the Windows build machine.--remote-dir is the build directory on that host. Use an absolute path with
forward slashes and no spaces.--angle-dir is the directory on that host that contains the UWP ANGLE DLLs.You can also set GEA_XBOX_BUILD_HOST, GEA_XBOX_BUILD_DIR and
GEA_XBOX_ANGLE_DIR instead of passing the flags.
To only generate and stage the build inputs locally, without the remote build:
npm run build:xbox -- --stage-only
To install and start the game on your Xbox, set GEA_XBOX_AUTH to your Device
Portal credentials as username:password, then run:
npm run deploy:xbox -- --xbox xbox-address
You can set GEA_XBOX_HOST instead of passing --xbox.
| Command | What it does |
|---|---|
npm run dev | Starts the browser version with live reload |
npm test | Runs the unit tests with Vitest |
npm run check | Type-checks the game |
npm run check:native | Type-checks the code used by the native builds |
npm --prefix web run check | Type-checks the browser entry point |
The code is in src/:
| Directory | Contents |
|---|---|
game/ | Game loop, energy, levels, and star and bomb waves |
actors/ | The flying mascot, stars, bombs and particles |
world/ | Terrain ring, sky, lighting, wind and the day-night cycle |
worldgen/ | Procedural generation of terrain, roads, rivers and scenery |
hud/ | Title screen, distance, energy and level display |
io/ | Input and audio |
kit/ | The 121 embedded low-poly models |
config.ts | Gameplay tuning values such as speed, distances and pools |
The browser entry point is web/main.ts. The native entry point
is index.tsx. The Xbox host code is in xbox/.
Skytail builds on The Aviator by Karim Maaloul, published with Codrops in 2016.
The models and favicon come from Coyotiv. The audio was generated with ElevenLabs. The native builds are made with GeaStack.
The code is MIT licensed. Third-party source credits and license terms are in LICENSE.third-party. The Inter and Unbounded font licenses are in LICENSE.fonts.