A lightweight pixel art and environment simulation framework for HTML Canvas, perfect for retroโstyle visuals and smooth motion design animations.
- ๐ Pixelโperfect rendering for authentic retro aesthetics
- ๐ฆ Environment simulation: forces, collisions and physics
- ๐งฉ Modular API for drawing, animating, and composing scenes
- โก Optimized for performance in modern browsers with webgl
- ๐ฆ Typescript with zero runtime dependencies
Here will be listed all the known applications using this framework in any creative way:
- ๐จ Paxel Motion App : a free drawing and motion graphics pixel art application, to let everyone experiment with retro visuals and imagination. Is developed and maintained by the same developer of the framework. Give it a try: Paxel Motion App.
This library is not published on npm. You can install it in two ways:
- Clone and build manually
git clone https://github.com/fpetcast/paxel-motion-framework cd paxel-motion-framework npm install npm run build - Download the build folder directly : the build folder is updated with the latest stable release. Copy it directly into your project and import from there.
The build folder contains the index file of the library, the minified version and the .d.ts for types.
For an immediate start you can just import the renderer class and initialize with a canvas html
import { PaxelRenderer } from "./build";
const canvas = document.createElement("canvas");
document.body.appendChild(canvas);
const paxelRenderer = new PaxelRenderer(canvas);For advanced configuration, you can import the type to pass optional config object as second argument to the class
import { PaxelRenderer, type PaxelRendererConfig } from "./build";
const canvas = document.createElement("canvas");
document.body.appendChild(canvas);
export const config: PaxelRendererConfig = {
init: true, // initialize without calling the init method
canExport: true, // let you export frames
defaultLayer: "default" //default layer name
canvas: {
width: 640, // will set canvas width
height: 640, // will set canvas height
},
grid: {
rows: 16, // number of rows
columns: 16, // number of columns
},
};
const paxelRenderer = new PaxelRenderer(canvas, config);Now let's begin to draw something on the screen: calling this function after initialization to display a frame border around the canvas.
function effect = (
paxelRenderer: PaxelRenderer,
config: PaxelRendererConfig,
intervalDuration: number = 100,
color: string = "#000000" //rgba or hex color
) {
const maxX = config.grid.rows;
const maxY = config.grid.columns;
let x = 0;
let y = 0;
const interval = setInterval(() => {
if (x < maxX) {
paxelRenderer.putPixel(x, 0, color);
paxelRenderer.putPixel(maxX - x, maxY - 1, color);
x++;
}
if (y < maxY) {
paxelRenderer.putPixel(0, y, color);
paxelRenderer.putPixel(maxX - 1, maxY - y, color);
y++;
}
if (y >= maxX && x >= maxX) {
clearInterval(interval);
}
}, intervalDuration);
}Using putPixel method we can show pixels at specific positions based on the grid defined in configuration.
It's also possible to draw using the position in pixel relative to the screen, for example detecting the click and draw pixels at grid positions using drawAt method
import { PaxelRenderer } from "./build";
const canvas = document.createElement("canvas");
document.body.appendChild(canvas);
const paxelRenderer = new PaxelRenderer(canvas);
const selectedColor = "#000000";
canvas.addEventListener("click", (e) => {
paxelRenderer.drawAt(e.offsetX, e.offsetY, selectedColor);
});By default all the pixels will be drawn on the same layer, but there is a stack mechanism to create overlays and complex backgrounds using layers. You can add a layer with a given name using addLayer and use it as the new active layer with setActiveLayer.
const newLayerName = "above-default-layer";
paxelRenderer.addLayer(newLayerName);
paxelRenderer.setActiveLayer(newLayerName);The active layer will be used by default when calling the drawing pixels functions putPixel and drawAt. There are other common features relative to layers like getLayers to get the names of all registered layers or removeLayerByName to remove them by name
const removeLayerName = "remove-me";
paxelRenderer.removeLayerByName(removeLayerName);Another important feature of layers is that they can be cleared from all the pixels rendered on them. To clear a single layer, just call clearLayer method, but as a good shortcut to completely clear the frame clearAllLayers should do the trick.
const clearLayerName = "clear-me";
paxelRenderer.clearLayer(clearLayerName);
// this should remove all pixels from the frame and layers
paxelRenderer.clearAllLayers();The visibiltiy of layers could be managed using setLayerVisibility.
const notVisible = "not-visible";
paxelRenderer.setLayerVisibility(notVisible, false);One of the coolest things you can do is to make static objects start moving, like a magician, or nowadays like animators.
The frameworks supports an animation loop that can be controlled as you like using three methods.
paxelRenderer.start(); // start physics simulation
paxelRenderer.stop(); // stop physics simulation
paxelRenderer.reset(); // restart physics simulation from original positionsBut playing a physics simulation will involve to register forces and possibly loops, to see all the pixels on the specified layers moving with a pattern.
Start simple, set a force on the renderer, this by default will only create the vector which will be added to the position of the layers. Only the layers that are subscribed to the force will be impacted when playing the simulation.
// this will create a force in the simulation with the specificed vector and a name
const force = {
x: 1,
y: 0
};
paxelRenderer.createForce(force.name, force);
// create a layer or register one yet created to the force
const movingLayer = "moving-layer"
paxelRenderer.applyForce(movingLayer);
paxelRenderer.start(); // to see the force in actionYou can also define the target FPS (but really always depends on the web browser), and the loop time. Applying the loop on specific layers will trigger a restart for all the pixels involved, that will return to the original positions like in an animation cycle.
// maxFps set to 30
let fps = 15;
paxelRenderer.setFPS(fps);
// define the loop time in seconds
let seconds = 2;
paxelRenderer.setLoopDuration(seconds);
const loopedLayer = "looped-layer"
paxelRenderer.applyLoop(loopedLayer);
paxelRenderer.start(); // to see the layer looping cycle