A world that stays manageable
Keep menus, levels, entities, groups, cameras, and collisions in a lifecycle that is easy to reason about.
Flixel-Pixi handles the game loop, rendering, input, collisions, sound, and all the fiddly browser work. You get to spend that time making the game feel good.
npm install flixel-pixi@next pixi.js@^8.19.0The core is just 137 kB gzipped.
import { FlxBackdrop, FlxG, FlxSprite, FlxState, FlxText } from 'flixel-pixi';
import {
FLOOR_Y,
makeCollectibleGraphic,
makeGroundTile,
makeRunnerSheet,
makeSkyTile,
PLAYER_FLOOR_Y,
PLAYER_HEIGHT,
PLAYER_WIDTH,
VIEW_HEIGHT,
VIEW_WIDTH,
} from './art';
export class HeroRunnerState extends FlxState {
player!: FlxSprite;
scoreText!: FlxText;
#collectibles: FlxSprite[] = [];
#jumpRequested = false;
#score = 0;
#nextCollectibleGap = 0;
requestJump(): void {
this.#jumpRequested = true;
}
override create(): void {
super.create();
FlxG.camera.bgColor = 0xff090d16;
const sky = new FlxBackdrop(makeSkyTile(), 0, 0, VIEW_WIDTH, VIEW_HEIGHT);
sky.repeatY = false;
sky.scrollVelocity.x = -13;
this.add(sky);
const ground = new FlxBackdrop(
makeGroundTile(),
0,
FLOOR_Y,
VIEW_WIDTH,
VIEW_HEIGHT - FLOOR_Y,
);
ground.repeatY = false;
ground.scrollVelocity.x = -92;
this.add(ground);
this.player = new FlxSprite(52, PLAYER_FLOOR_Y);
this.player.loadGraphic(
makeRunnerSheet(),
true,
false,
PLAYER_WIDTH,
PLAYER_HEIGHT,
);
this.player.addAnimation('run', [0, 1, 2, 3], 11, true);
this.player.play('run', { loop: true, speed: 11 / 60 });
this.player.acceleration.y = 680;
this.player.maxVelocity.y = 420;
this.add(this.player);
this.#makeCollectible(224, 102);
this.#makeCollectible(292, 78);
this.#makeCollectible(366, 102);
this.scoreText = new FlxText(9, 8, 150, 'GEMS 000');
this.scoreText.setFormat(undefined, 9, 0xfff8fafc, 'left');
this.scoreText.scrollFactor.make(0, 0);
this.add(this.scoreText);
}
override update(): void {
const grounded = this.player.y >= PLAYER_FLOOR_Y - 0.5;
if (this.#jumpRequested && grounded) {
this.player.velocity.y = -285;
this.player.play('run', { loop: true, speed: 7 / 60 });
}
this.#jumpRequested = false;
super.update();
if (this.player.y >= PLAYER_FLOOR_Y) {
this.player.y = PLAYER_FLOOR_Y;
this.player.velocity.y = 0;
this.player.play('run', { loop: true, speed: 11 / 60 });
}
for (const collectible of this.#collectibles) {
if (collectible.x + collectible.width < 0) {
this.#recycleCollectible(collectible);
}
if (this.player.overlaps(collectible)) {
this.#score += 1;
this.scoreText.text = `GEMS ${this.#score.toString().padStart(3, '0')}`;
FlxG.camera.flash(0x4412d9e6, 0.12);
this.#recycleCollectible(collectible);
}
}
}
#makeCollectible(x: number, y: number): void {
const collectible = new FlxSprite(x, y);
collectible.loadGraphic(makeCollectibleGraphic());
collectible.velocity.x = -92;
collectible.angularVelocity = 90;
this.#collectibles.push(collectible);
this.add(collectible);
}
#recycleCollectible(collectible: FlxSprite): void {
this.#nextCollectibleGap += 37;
collectible.x = VIEW_WIDTH + 54 + (this.#nextCollectibleGap % 76);
collectible.y = this.#nextCollectibleGap % 2 === 0 ? 102 : 78;
}
}
Take the guided route, learn from a working project, or look up the one thing blocking you right now.
Set up the engine, put something on screen, and get a tiny game running before the coffee goes cold.
Make a first sceneOpen a working example, play it, change it, and see how the engine pieces fit together.
Explore working gamesJump straight to a class, method, or type without digging through a long tutorial first.
Search the APIThe useful parts are already connected, so a quick prototype can grow into a real project without a rewrite halfway through.
Keep menus, levels, entities, groups, cameras, and collisions in a lifecycle that is easy to reason about.
Add gravity and velocity, separate solid objects, tune elasticity, detect overlaps, and keep busy worlds quick with spatial collision.
Animate sprites, shape motion with tweens, shake the camera, and keep adjusting until the jump feels right.
Burst sparks, dust, smoke, trails, and debris from pooled emitters, then layer in filters, fades, flashes, and camera shake.
Map keyboard, pointer, touch, and gamepad controls to the same game actions, with rebinding when you need it.
Mix music and effects in their own groups, loop ambient beds, and attach sounds to waterfalls, engines, or anything moving through the world.
Scale, align, and letterbox the game across phones, tablets, and desktops, with safe-area-aware layouts and fullscreen support.
Preload assets, save progress, record deterministic replays, inspect the game in the debugger, and get the build onto the web.
This little invaders game is built with the same public API in the guide. Click the canvas, move around, fire a few shots, then open the example and pull it apart.
Use the arrow keys or A and D to steer. Press Spacebar to fire. Click the game first to focus it.
One state. One sprite. One idea you want to feel in your hands. We can build outward from there.