๐พ Easily make 2D browser games with pixi.js
- Reactive - Re-render view when state changes
- Pooling - Create all game object up front - in render never add or remove game objects
- Timer - Run logic every X ticks
- Animations
- Debug overlay
- Keyboard input
- Sounds
- Sprite sheet generation
- Scenes
- CLI - Create project and components
- CI - Workflows to build, test and deploy to
itch.io
- Screen shake
- 100% Type-Safe
- Batteries included (get started using one CLI command)
- Uses
vite
for a super fast and modern dev server
- State machine (Recommendation: xstate)
- Noise (Recommendation: simplex-noise)
- Physics
- Network
npx alchemy-create@latest create <game-name>
import { sprite } from "alchemy-engine";
sprite(container, textures["./square-1"]);
import { animatedSprite } from "alchemy-engine";
animatedSprite(container, [textures["./square-1"]]);
import { text } from "alchemy-engine";
text(container, textStyle, "Hello world");
import { htmlText } from "alchemy-engine";
htmlText(container, textStyle, "Hello world");
import { bitmapText } from "alchemy-engine";
bitmapText(container, textStyle, "Hello world");
import { container } from "alchemy-engine";
container(_container);
import { graphics } from "alchemy-engine";
graphics(container);
import { rectangle } from "alchemy-engine";
rectangle(container, { x: 0, y: 0, width: 10, height: 10 });
import { onClick } from "alchemy-engine";
onClick(container, () => {
console.log("Clicked!");
});
import { onHover } from "alchemy-engine";
onHover(container, {
onOver() {
console.log("Hovered!");
},
onOut() {
console.log("Not hovered!");
},
});
Constants for all arrow keys
import { arrowKeys } from "alchemy-engine";
export const keys = ["a", "w", "s", "d", ...arrowKeys] as const;
TODO
TODO
TODO
Nicely log a Pixi object. Set label
property for best result.
import { logObject } from "alchemy-engine";
const sprite = new Sprite();
sprite.label = "sprite";
logObject(sprite);
Enables easier logging of sprite bounds
import { boundsToString } from "alchemy-engine";
console.log(boundsToString(sprite));
Check if a point is within the bounds of an object
import { contains } from "alchemy-engine";
if (contains(sprite, { x: 1, y: 1 })) {
// point is within bounds of sprite
}
Check if the bounds of two objects are intersecting
import { intersects } from "alchemy-engine";
if (intersects(sprite1, sprite2)) {
// sprites are intersecting
}
import { isAnimatedSprite } from "alchemy-engine";
if (isAnimatedSprite(sprite)) {
// sprite is of type AnimatedSprite
}
This function can be used to for example load a level from image data
import { loadDataFromImage } from "alchemy-engine";
import map from "~/public/asset/map.png?url";
const { pixels, width, height } = await loadDataFromImage(map);
console.log(pixels);
// ['255-255-255', '0-0-0']
The arguments passed to a scene
{
textures,
container,
input,
state,
timer,
sound,
app,
timer,
useScreenShake,
}: Scene
An object containing all textures by name
Record<TextureName, Texture>;
function myScene({ textures, container }: Scene) {
sprite(container, textures["./square1"]);
}
Get multiple textures
function myScene({ getTextures, container }: Scene) {
const textures = getTextures(["./texture-1", "./texture-2"]);
animatedSprite(container, textures);
}
Enable the use of screen shake
const screenShake = useScreenShake(container);
screenShake.add(0.5);
- sine
- easeOut
- easeIn
These functions all require an onUpdate
and duration
argument
Optionally you can pass a startValue
(default: 0) and endValue
(default: 1)
- center
The Pixi Application
instance
Record<MusicName, Howl>;
Record<SoundName, Howl>;
setScene("mainMenu");
timer
A timer that doesn't get cancelled when changing scenes
A scene specific Pixi container. Will be destroyed when scene is changed.
Set state to trigger sync
and subscribe
functions
Re-exported from valtio
delay
// Wait 100 updates
await delay(100);
repeatUntil
Execute a callback every update until duration
is reached
await repeatUntil(3, (time, deltaTime) => {});
repeatEvery
Execute a callback forever every interval
updates
Returns a cancel
function
const cancel = repeatEvery(3, (time, deltaTime) => {});
debouncedKey
debouncedKey(
// Key id
"d",
// Callback
() => {
s.position.x += 1;
},
// Delay between key presses
10
);
isKeyDown
Check if a key is currently being pressed
repeatEvery(1, () => {
if (isKeyDown(["a", "ArrowLeft"])) {
s.position.x -= 1;
}
if (isKeyDown(["d", "ArrowRight"])) {
s.position.x += 1;
}
});
There is a built-in seedable random module.
To set the seed, pass it in to createGame
The module uses the same API as park-miller, with some additions:
chance(percentage) => boolean
Start the dev server. Listens to changes to source code, sprite
, src/public/asset/sound
and src/public/asset/music
folders.
npx alchemy-cli dev
Generate sprite sheet
npx alchemy-cli sprite
Load sounds
npx alchemy-cli sound
Run ./go.sh
to test that things work