npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

alchemy-engine

v0.3.0

Published

Create 2D browser games with pixi.js

Downloads

62

Readme

:alembic: alchemy engine

👾 Easily make 2D browser games with pixi.js

:sparkles: Features

  • 100% Type-Safe
  • Batteries included (get started using one CLI command)
  • Reactive - Re-render view when state changes
  • Object pooling
  • 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
  • Uses vite for a super fast and modern dev server

Getting started

Run the init command from within a Git repo

npx alchemy@latest init

This will:

  • Copy template files
  • Add alchemy-engine and pixi.js as dependencies

API Docs

Module API

These are imported directly from alchemy-engine

create

Convenience functions to create Pixi objects

sprite

import { sprite } from 'alchemy-engine'

sprite(container, getTexture('./square-1')])

animatedSprite

import { animatedSprite } from 'alchemy-engine'

animatedSprite(container, getTextures(['./square-1', './square-2']))

text

import { text } from 'alchemy-engine'

text(container, textStyle, 'Hello world')

htmlText

import { htmlText } from 'alchemy-engine'

htmlText(container, textStyle, 'Hello world')

bitmapText

import { bitmapText } from 'alchemy-engine'

bitmapText(container, textStyle, 'Hello world')

container

import { container } from 'alchemy-engine'

container(_container)

graphics

import { graphics } from 'alchemy-engine'

graphics(container)

rectangle

import { rectangle } from 'alchemy-engine'

rectangle(container, { x: 0, y: 0, width: 10, height: 10 })

event

onClick

Convenience functions for mouse input

import { onClick } from 'alchemy-engine'

onClick(container, () => {
  console.log('Clicked!')
})

onHover

import { onHover } from 'alchemy-engine'

onHover(container, {
  onOver() {
    console.log('Hovered!')
  },
  onOut() {
    console.log('Not hovered!')
  },
})

sync

sync

syncPosition

keys

arrowKeys

Constants for all arrow keys

import { arrowKeys } from 'alchemy-engine'

export const keys = ['a', 'w', 's', 'd', ...arrowKeys] as const

position

TODO

getAllChildren

TODO

getAllLeafChildren

TODO

debug

logObject

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)

boundsToString

Enables easier logging of sprite bounds

import { boundsToString } from 'alchemy-engine'

console.log(boundsToString(sprite))

contains

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
}

intersects

Check if the bounds of two objects are intersecting

import { intersects } from 'alchemy-engine'

if (intersects(sprite1, sprite2)) {
  // sprites are intersecting
}

type guards

isAnimatedSprite

import { isAnimatedSprite } from 'alchemy-engine'

if (isAnimatedSprite(sprite)) {
  // sprite is of type AnimatedSprite
}

loadDataFromImage

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']

pool

Docs


Scene API

The arguments passed to a scene

{
  textures,
  container,
  input,
  state,
  timer,
  sound,
  app,
  timer,
  useScreenShake,
}: Scene

getTexture

Get a texture

function myScene(scene: Scene) {
  sprite(scene.container, scene.getTexture('./texture-1'))
}

getTextures

Get multiple textures

function myScene(scene: Scene) {
  animatedSprite(
    scene.container,
    scene.getTextures(['./texture-1', './texture-2']),
  )
}

useScreenShake

Enable the use of screen shake

screenShake takes a number between 0 and 1

const screenShake = useScreenShake(container)

screenShake(0.5)

animate

These functions all require an onUpdate and duration argument

Optionally you can pass a startValue (default: 0) and endValue (default: 1)

  • sine
  • easeOut
  • easeIn
  • linear

app

The Pixi Application instance

music

Record<MusicName, Howl>

Example:

scene.music.bgm.loop(true).play()

sound

Record<SoundName, Howl>

Example:

scene.sound.coin.play()

setScene

Takes sceneKey as an argument

setScene('mainMenu')

global

timer

A timer that doesn't get cancelled when changing scenes

container

A scene specific Pixi container. Will be destroyed when scene is changed.

state

Set state to trigger sync and subscribe functions

internalState

subscribe, subscribeKey, proxy

Re-exported from valtio

Changes from valtio versions:

  • Triggers once up front when called
  • Unsubscribes when changing scene

timer

delay

Resolves a promise after X ticks

// Wait 100 updates
await scene.timer.delay(100)

repeatUntil

Execute a callback every update until duration is reached

await scene.timer.repeatUntil(3, (time, deltaTime) => {})

repeatEvery

Execute a callback indefinitely every interval updates

Returns a cancel function

const cancel = repeatEvery(3, (time, deltaTime) => {})

input

debouncedKey

scene.input.debouncedKey(
  'd',
  () => {
    s.position.x += 1
  },
  // Delay between key presses
  10,
)

isKeyDown

Check if a key is currently being pressed

scene.timer.repeatEvery(1, () => {
  if (scene.input.isKeyDown(['a', 'ArrowLeft'])) {
    s.position.x -= 1
  }
  if (scene.input.isKeyDown(['d', 'ArrowRight'])) {
    s.position.x += 1
  }
})

random

There is a built-in seedable random module.

To set the seed, pass it in to createGame

createGame({
  randomSeed: 99,
})

The module uses the same API as park-miller, with some additions:

chance(percentage) => boolean


CLI

dev

Start the dev server. Listens to changes to source code, sprite, src/public/asset/sound and src/public/asset/music folders.

npx alchemy dev

sprite

Generate sprite sheet

npx alchemy sprite

sound

Load sounds

npx alchemy sound

Local development

Run ./go.sh to test that things work


See also