Changelog
All notable changes to this package are written here. The format follows Keep a Changelog, and versions follow Semantic Versioning; before 1.0 a minor version may break the API.
0.6.6
Added
TextureRegistry.addView(key, page, x, y, w, h): a key that is one rect of another texture, on that texture's own source (CanvasTexture'sview), so pieces swapped every few frames batch together.
Changed
paintLifeArtlays every piece of the ambient life (leaves, seeds, embers, motes, ash, butterflies, birds, fireflies, smoke) on one page and registers each key as a view of it (packShelves). A canvas each made every flap of a butterfly or puff of smoke a texture of another source, and Pixi rebuilt the world's draw instructions at each. The keys andLifeArtare unchanged.
0.6.5
Changed
Group.moveAbove/moveBelowdo nothing when the child is already directly above / below the other. Taking it out and putting it back marked the render group's structure changed, so Pixi rebuilt its draw instructions that frame: a game ordering a held weapon against its body every frame rebuilt them every frame.
0.6.3
Changed
SharpTextlays a label's stand-in again only when its home moved, hid, faded or changed depth:setFromMatrix(a decomposition with trigonometry that also marks the transform changed for Pixi) ran for every label every frame, and a town's standing NPC names took ~9% of a frame on a CPU throttled 6x.
0.6.2
Added
Stage.pixelDensity: texels of the pixel-perfect grid to a world pixel (1 by default). A game drawing characters at two art pixels to a world pixel sets 2, and the grid keeps their detail instead of sampling them back down.
0.6.1
Added
- Pixel-perfect world:
Stage.pixelPerfectdraws the world layer into a texture of one texel per world pixel, its origin on a whole world pixel, and shows it at the camera's zoom moved by the scroll's fraction, so turned, scaled and between-pixels art lands on one grid while the camera still glides by backing pixels (PixelGrid,gridPlacement). Text made byadd.text/add.labelis lifted onto a layer at screen resolution over the world, following its container's transform, visibility and alpha (SharpText;Stage.keepSharpfor other text).Camera.snapRootlands an object's root on whole world pixels while it is on, and onsnapotherwise.
0.6.0
Added
- Interest management:
Roomtakesinterest: { radius, leaveMargin, canSee }and sends each player only the bodies near it, with a margin so one on the edge is not spawned and removed over and over.room.place(id, x, y).SpatialHash(points in square cells,nearandinRect) is exported for a game's own use. - Lag compensation:
RoomtakesrewindMsand keepshistory, aLagCompensatorrecorded as each round of snapshots goes out and blended asInterpolationBufferblends, so a shot is judged against what the shooter saw:at,all,near,alongRay.RoomHost.rewindTime(claimed)andLagCompensator.clampTimehold a client's claim within the window;InterpolationBuffer.viewTime(now)is the time a client sends with a shot. - Server bodies:
Room.addBody(data, x, y)andremoveBodyfor monsters, projectiles and crates the server moves, sent beside the players with the same interest and rewind history (RoomBody,RoomSeen,BODY_ID_BASE).canSeenow gets either kind. - Rebinding the gamepad:
Keymapkeeps a first binding per device, so pad buttons are rebindable beside keys (keysFor(action, device));InputCapturewaits for the next key, pad button or stick push for a settings screen;padLabelanddeviceOf. - Input replays:
InputRecorderkeeps anActions' steps as a JSONInputTape(changes only);InputPlayerplays one back throughActions.replay, exactly.Actions.taphands every step's values to a listener. - Tools: the
dialogueexample, a playground that checks a script as it is typed and plays it with its variables in view;formatTraceprints a behaviour tree's last tick. - Crowds:
flowFieldandfollowField(one search for a crowd, byfindPath's own rules), and steering:seek,flee,arrive,separation,alignment,cohesion,followFlow,combineSteering,applySteering. - Behaviour trees:
BehaviorTreeandBtAgent(one tree, many agents, each with its own memory),sequence,selector,reactiveSequence,reactiveSelector,parallel,invert,succeed,repeat,retry,cooldown,timeout,wait,condition,actionwithonHalt, a trace for debugging, andbuildTreefrom JSON. - Dialogue:
DialogueRunnerruns a branching script written as data (lines, choices with conditions,onceand locked choices, variables, commands,{…}in text, translation, save and restore);validateDialoguechecks a script;compileExpris its safe expression language. - Prefabs:
definePrefab,prefabPart,spawnPrefabandisPrefabOf, with variants laid over their base field by field;PrefabLibraryloads them from data. - Sounds in the world:
SpatialAudioplaces any buffer at a point, panned, faded and muffled by distance from a moving listener, loops paused out of range and picked up where they would be;spatialMixis the mix on its own. 1681-engine/mobile:capacitorConfig(a Capacitor config with a game's defaults),BackStack,doubleBackToExitandinstallBackButtonfor Android's back button,safeAreaInsets,keepScreenOn, andnativeBuildOfforpickWebUpdate. Capacitor is handed in, never imported.
Changed
RoomInterest.canSee(viewer, other)gets aRoomSeen(a player or a body the server moves), told apart byother.kind;RoomPlayerhaskind: 'player'.RoomandRoomHosttake a second type parameter for bodies' data.Keymap.bindreplaces the action's first binding of the same device (a pad input its first pad input, a key its first key) instead of its first binding of any kind. Keymaps of keys only behave as before.
Fixed
WebPatcher.tidycalled while the tidystartEntrybegan was still running deleted the same folders twice at once; one failed, and the old webs after it were left on disk. Calls now run one after another.ScrollView.measureread the content as no taller than the view (Pixi cuts a masked container's bounds to its mask), so a view measured that way never scrolled. It now measures the content without the clip.
0.5.10
Added
- LDtk import:
parseLdtkreads a project's levels into the sameTileMapTiled maps use, soTilemapView,collisionFromTiledandobjectsOfwork on them: IntGrid, AutoLayer, Tiles and Entities layers, entity and level fields, tile custom data and enum tags, world layouts, multi-worlds projects and separate.ldtkllevel files.levelByName,levelAt,entitiesOf,intGridAt,ldtkExternalLevels,loadLdtkLevel;AssetLoader.ldtk(key, url, { levels })andldtkProject(key);collisionFromIntGrid(grid, solid). - Autotiling:
autotile,updateAutotileandpaintTerrainon 4-bit edge masks or 47-tile blob masks (edgeMask,blobMask,BLOB47_MASKS,blob47Index), with stable variants, fallbacks andconnectsTo; an edit retiles only its 3x3 neighbourhood.TilemapView.refreshTiles(layer, cells)repaints only the chunks touched, andx/yplace a map at a world position. Theldtkexample. - Ambient life:
Drift(leaves, seeds, embers, motes, ash),Birds,Butterflies,FirefliesandChimneySmoke, bounded pools that live only in view on whole pixels, spawned from aLifeSourcecallback (sourceOf,pickInView) under aLifeAir;paintLifeArtin1681-engine/pixelpaints their art. HapticsandHapticGate: a per-kind rate-limited buzz with an injectable player (navigator.vibrateby default).ApiClientandApiError: a JSON REST client with a timeout, a bearer token, a 401 refreshed once and retried, status and error hooks, and abort.PrepainterandworkerPainter: big textures painted ahead in a worker for the neighbours of the current key, the rest freed.- React kit:
ConfirmButton(a button asked twice),useJsonSelector,useStoreJson, the optional labelconfirmAgain. 1681-engine/updates: the pure half of file-by-file web updates from a release store, shared by desktop, browser and phone builds —LatestJson/WebManifest/WebFile,validManifest,planWeb,pickWebUpdate,apkUpdateOf,pluginEntries,heldBundle.1681-engine/electron, the main-process side of an Electron build:WebPatcher(each newer web built beside the running one, checked by sha256, only for the same shell; one that never boots is dropped for the last good one),shellId,SessionStore/registerSessionStore(an OS-encryptedsafeStoragesecret, refused underbasic_text),lockDownWindow,denyPermissions,refuseDebugLaunchand the pure link rules. Both subpaths ship as ES modules and CommonJS (dist/cjs);electronis an optional peer dependency that nothing imports.- Browser tests (
npm run test:browser, Vitest browser mode on headless Chromium with SwiftShader): WebGL pixels read back, canvas text,FontFace,decodeAudioData, TexturePacker atlases, silhouettes,RichText,PixelLayer,PixelMarks,Darkness, the ReactPanel's fitting, fullscreen and pointer lock,Engine.createandDrawCallCounter, in a CI job of their own;npm run checkstays node-only. - Guide page "Desktop: patching, sessions, lockdown"; the tilemap page covers LDtk and autotiling.
Fixed
AssetLoadernames the URL when an image, sprite sheet or atlas page cannot be decoded, and the family and URL when a font cannot be loaded; the browser's bare message named neither.
0.5.9
Added
- Platform helpers:
enterFullscreen/exitFullscreen/toggleFullscreen/isFullscreen/fullscreenSupported/onFullscreenChange(with webkit fallbacks),lockOrientation/unlockOrientation/currentOrientationKind,prefersReducedMotion/onReducedMotionChange/followReducedMotion,requestPointerLock/exitPointerLock/isPointerLocked/onPointerLockChange,onAppResumeandtimeoutSignal(anAbortSignal.timeoutstand-in for older iOS); each does nothing safely where unsupported. Engine.create({ pauseWhenHidden, followReducedMotion })(both off by default): pause while the page is hidden with no catch-up steps on return, and copy the system's reduced-motion setting intoengineSettings.reducedFx.HiddenPauseholds the rule on its own.- Gamepad rumble:
rumble,rumblePad,stopRumble,rumbleParamsandActions.rumble.Pointergainslock,unlock,lockedandtakeMovementfor mouse-look. AssetLoaderloadsatlas(TexturePacker/Pixi JSON, hash or array, multipack, trimmed and rotated frames;getAtlas),font(aFontFaceintodocument.fonts) andaudio(getAudio/decodeAudio, decoded through the sound context or kept until one exists).parseAtlas,collectAtlasPages,duplicateFrames,TextureRegistry.addAtlas,CanvasTexture.addPacked,SoundEngine.audioContext.RichText(1681-engine/ui): inline markup for colour, bold, italic, size and icons, wrapped and aligned, on canvas or bitmap fonts; the pureparseRichText,escapeRichText,stripRichTextandlayoutRichText.- Hit flashes and dash ghosts without a filter pass:
solidSilhouette,fillSolid,clearSilhouettesand pooledAfterimages. - Pixel-art zoom maths:
worldZoom,baseZoom,zoomSteps,zoomResolution,fitScale(whole steps and an optional half step with the resolution that keeps a world pixel whole). blendLayer: one container per stage for additive sprites, so they do not split batches.- In
1681-engine/pixel:spark,tail,pixelOn;PixelMarksandaimPixels(chevron aim lines and lock rings in world pixels). Connection:fatalCloseCodes(close codes that never reconnect) andresumeProbeMs(a socket that does not answer a ping after the app returns from the background is dropped withCLOSE_NO_ANSWERand reconnected);reconnectDelayandshouldReconnect.- Guide pages: assets, platform, ECS, rig, tilemaps, audio, rich text, netcode.
Changed
- Movement numbers no longer need a fork:
MovementParamstakesdashFlagandstepDt(defaultsINPUT_FLAG_DASH, 1/60 s); the tile size and body half extents were already the grid's.TILE_SIZE,BODY_HALF_*andINPUT_FLAG_DASHare kept as the defaults. Darknesstakes{ carriedLight }(defaultCARRIED_LIGHT, 80) andupdate()'s radius defaults to it; the pixel module no longer importsnet/.Clockruns timers without allocating a frame; a timer that throws is reported toClock.onErrorand dropped.DrawCallCountercountsdrawRangeElementsand leaves a later wrapper in place when it uninstalls.- A socket replaced by a newer one no longer emits a stale
close. - The package ships no source maps (
ENGINE_SOURCEMAP=1builds them; they pointed at an unpublishedsrc/) and no longer declaresengines.node;Room,RoomHostandLobbyHoston a server need Node 22 or later. The tarball went from 931 kB to about 420 kB. - Typechecks against pixi.js 8.6, the peer floor, tested in CI along with the examples build. Workflows use v5 actions; publishing pins npm 11 and uses no cache.
Tests
- Golden bytes for the snapshot codec, cadence,
LobbyRooms,Connection, the platform helpers, sparse sets, field columns, sticks, long press, eases, synth, view culling, touch detection, the spatial hash and transient lights.
0.5.8
Added
- Bodies that turn:
PhysicsWorldbodies made withrotates: truehave anangle, anangularVelocity, a moment of inertia from their shape and are turned by contacts (each with one or two contact points:ContactInfo.points),applyImpulse/applyForceat a point,applyTorque,applyAngularImpulse;angularDamping,setAngle,velocityAt,toWorld/toLocal. Convex polygons (polygonShape,polygonCentroid,regularPolygon, up to 16 corners) and boxes at an angle collide by the separating axis test with clipped edges; turning bodies never push out of a merged tile box's internal face, so seams stay snag-free. Kinematic spinners, rolling balls, raycasts against turned shapes, sleep that counts spin.syncToBodyturns a view with its body. Bodies that do not rotate behave exactly as before. 1681-engine/postfx:PostFxlays a chain of full-screen filters over the whole picture or the world layer, switched one by one, withflash,shockwaveandanimatefor effects that come and go (skipped under reduced effects);BloomFilter,VignetteFilter,ColorAdjustFilter,ChromaticAberrationFilter,CrtFilter,PixelateFilter,ShockwaveFilter;FxFilterbuilds an effect from one spec in GLSL and WGSL with shared helpers.- Lobby and matchmaking:
LobbyHost(transport-free likeRoomHost): rooms with a host, a share code, private rooms, ready flags, kick and promote, chat, the game's settings, a room starting its own game; a queue per mode filled byMatchmaker(pure: rating windows widening with the wait, both sides' windows respected, parties never split, teams balanced bybalanceTeams); ready checks with a deadline, decline lockouts and requeueing at the old place;onStarthands each player where to go,finishopens the rooms again; message rate limits.LobbyClientkeeps the state for a lobby screen over aConnection. - React kit:
UiProvider classNameshangs a game's own class names beside the kit's ({ tabs: 'tabs', button: 'btn', picked: 'active' }), states included, so a game whose stylesheet already styles its own names takes the kit without rewriting it;useCx/UiSettings.cxjoin them,classJoineris the pure rule. DebugOverlay'scolliderscallback also gets a line drawer, anddrawCollidersdraws turned shapes as outlines.- Examples:
physics-toys(turning bodies under every post effect) andlobby(a React lobby screen, a server with bots). Guide pages: post-processing, lobby and matchmaking; the physics page covers turning bodies.
0.5.7
Added
- Aseprite import:
parseAsepritereads Aseprite's sprite-sheet JSON (hash or array frames) intoAnimatorclips, one per tag, with every tag direction and repeat count, per-frame durations, trimmed frames, slices with their 9-slice centre and pivot (sliceAt,sliceBorders), layers, and cel user data as frame markers (clipMarkers);loadAseprite/addAsepriteTexturesregister the sheet's frames as textures. AnimStateMachinedrives anAnimatorfrom named states: transitions on bool, number and trigger parameters, clip-end and exit-time transitions, any-state transitions by priority, events on frame markers (a footstep on frame 3), no allocation per update;AnimStateComponentandanimStateSystemrun it in the ECS.Timeline: sequenced and overlapping tweens and calls with positions ('<','+=200', labels),parallelgroups, nesting, repeat/yoyo/repeatDelay, timeScale, pause/seek/reverse/restart,finished; made withstage.tweens.timeline()or stepped by hand.Tweenergainsrun(steppable)andtimeline().Parallax/ParallaxLayer: scenery that scrolls at a share of the camera's travel on x and y, tiled textures repeating forever (covering only the view), offset and drift, whole-pixel snapping on pixel-perfect engines, culling and depth; works with the camera's zoom.- Examples:
aseprite(a generated sheet, the state machine and its events); the platformer's hills are aParallaxand reaching the goal plays aTimeline. - Documentation: a guide page for every module (input, animation, physics, navigation, particles, game feel, ambient, tweens and parallax, transitions, UI, React kit, localisation, saves, diagnostics and debug); the API reference covers
1681-engine/uiand1681-engine/react.
Fixed
- The docs build no longer fails on
{{quoted in inline code of the API reference.
0.5.6
Added
- React kit (
1681-engine/react, React 19 as an optional peer dependency):Panel(a window fitted into the screen byfitToViewport, its body scrolling; a full-screen sheet on touch),TabStrip(wraps withwrap),SplitPanewithusePickedanduseFillSheet(list beside detail, one pane at a time on a narrow touch screen),Select(a drop-down whose list opens in a portal as wide as its longest label, placed bylistWidthandplaceTooltip),TooltipandTitleTips(everytitle=drawn as the kit's tooltip, flipped at the edges, desktop only),Modal,ConfirmDialog/askConfirm/askQuantity,ChoiceDialog/askChoice,Slider,PixelImage(sprites at whole multiples),Glyph(pixel marks a pixel font lacks,registerGlyph),UiProvider(touch mode, window scale, viewport, portal, labels) anduseStoreovercreateStore. Themed by CSS custom properties (--e1681-*, frames as 9-sliceborder-images) inbaseCss/injectBaseCss(); every word is a prop or a label with an English default. Pure placement helpers (windowStyle,cursorTip,listPlacement,pixelFit,splitModeFor) are exported and tested under node. Scene.delay(ms, fn): timers owned by the scene, cancelled when it exits (stage.time.delayedCalltimers belong to the stage and fire after the scene has gone).Scene.useInput(actions)registers input to flush when a scene pushed over this one pops.Actions.flush()drops pending press and release edges; held keys stay held but are not pressed again.- Key chords in bindings:
Shift+Tab,Ctrl+KeyS(Shift,Ctrl,Alt,Meta, either side). A held chord keeps the bare key's action off until that key comes up.parseChord,chordHeld;keyLabelreads a chord. Keymaptakesfixedinputs per action (gamepad, touch, a reserved key such as Escape for pause), laid after the player's keys byapplyToand never saved;fixedFor(action).Camera.clearBounds(),Camera.reset()(no target, no bounds,defaultZoom, scroll 0, no shake or flash),Camera.defaultZoom(set from the engine'szoom),trackingandboundsRect.PhysicsWorld.bodies(read-only), anddrawColliders(world, draw, colors?, colorOf?)forDebugOverlay'scolliderscallback.parentoptions:FloatingText(labels under a scene'sroot) andUiRoot(under a scene'shud, destroyed with it).ColorGradetakesparentandplacement: 'bottom', which keeps it under every HUD whatever the order they were made in.ColourSkin.fillAlphaandborderAlpha: a bare ring isfillAlpha: 0.Dialogwithout aheighttakes its content's, padding included, measured again on every layout.installLabelFont,BitmapLabel,Label.bitmap,ObjectFactory.labelandFloatingTexttakeglyphsa font bakes besides ASCII.
Changed
SceneManager.replaceresets the shared camera between the old scenes leaving and the new one entering, so a level's follow target and bounds no longer hold over the next scene.pushandpopleave it alone;popflushes the resumed scene's registered input beforeonResume.Keymap.applyToaccepts anActionswith more actions than the keymap's and binds only the keymap's.- A
ColourSkinborder is drawn as four strips round its fill rather than a box under it, so a see-through fill no longer shows the border colour through it (opaque skins look the same). Buttoncentres its content again when the content (aLabelgiven new text) changes size; widgets tell their parent throughWidget.childResized.- Documented:
Scene'sworld/root/hudand Pixi'sContainer.labelare taken names for subclass fields; aKeyboardknows only keys pressed after it was made; bitmap label fonts bake ASCII unless given more; the save envelope keeps its'lantern-save'tag so existing saves still load.
Fixed
DebugOverlayprevents its toggle key's browser action (F3 opened find-in-page).Sliderdraws its focus face under the knob, not over it.
0.5.5
Changed
- Renamed to 1681 Engine: the package is
1681-engine(1681-engine/pixel,1681-engine/ui) and the repositoryPhadec/1681-engine. The pixel module keeps its name, Dither. World.entities()andWorld.componentsOf(e)list live entities and what they hold, for tools.
Added
- UI (
1681-engine/ui): canvas widgets on the stage's screen layer (UiRoot,Panel9-slice or colour box,Button,Label,Barwith a delayed drain,Slider,Toggle,Stack,ScrollView,List, modalDialog), keyboard and gamepad focus throughActions, screen anchoring that follows resizes, whole-pixel layout, presses on widgets kept from the world (UiPointerGate); a DOM/React bridge (DomOverlay,DomWorldAnchors,createStore,InputShield); pure layout maths shared by both (stackLayout,anchorPosition,fitToViewport,placeTooltip,wrapRow,listWidth, scrolling, focus navigation). - Physics:
PhysicsWorld, rigid circles and axis-aligned boxes (dynamic, static, kinematic) with restitution, friction, damping, gravity scale, sensors and layers; a grid broad phase, warm-started impulses, sub-steps against tunnelling, optional sleeping; contact events,grounded,raycast, apreSolvehook;tileBodiesmerges a tile grid into static boxes;syncToBodydraws a view between steps. Not deterministic across machines. - Particles:
Emitter(bursts and rates, spread, gravity, drag, size/alpha/tint over life, normal or additive) on one pooledParticleContainer, a sharedParticleBudgetthat thins underreducedFx/batterySaver, culled off screen; pureParticleSimand curves. - Navigation: A*
findPathon any tile grid orCollisionGrid(reach radius, extra cost, end-only tiles, no corner cutting),straightenPath,simplifyPath,floodFill,nearestOpen,MinHeap. - Saves:
SaveStore(versioned slots over a pluggableSaveBackend, a migration chain, a checksum, the last good copy kept),Autosaver. - Scene transitions:
transitionTo(engine, next, transition, mode)withFadeTransition,CrossfadeTransition,DitherWipeTransition;Letterbox. - Localisation:
Translator(English source as key,|context, plurals, missing-key callback, runtime switching),extractStrings. - Ambient:
DayClock, deterministicweatherAt,WeatherLayer,ColorGrade; pixelNightShade,paintWeatherArt,SwayProp(sprites swaying by whole pixels),DitherVignette,nineSlice/frameSvgUI frames. - Input:
Keymap(rebinding, reserved keys, saved sets),AimGesture(drag-to-aim),TargetLock. - Diagnostics and debug:
watchContextLoss,guardRender,bootWatch,maxTextureSize,FrameSampler,captureErrors;DebugOverlay(FPS graph, draw calls, entities, collision boxes, the last errors). FloatingText: pooled bitmap labels for damage numbers and pickups, fonts baked ahead.
0.5.3
Added
- Music in layers:
music.channel(name)plays one looping track per channel (a score and an ambience under it), each crossfaded on its own at its own level;music.playis the'music'channel.music.configure({ route, pauseInBackground }): the<audio>element's own volume by default, a Web Audio gain only where that volume is read-only (iOS) or when asked, and the music stopped while the page is hidden.MusicChannel,elementVolumeLocked. paintLayer(stage, { at, duration, paint, depth, alive, every, fps, onCreate })in1681-engine/pixel: one effect as its own pixel layer redrawn at 12 fps, following its anchor, dithered away at the end and skipped off screen.Footsteps: steps and hooves by the surface under the feet, the surface named by the game (setSurfaceAt).detectTouchandTouchEnvare exported;setTouchOverrideKeynames thelocalStoragekey that forces touch.
Changed
MusicOptionsis now the player's configuration; a track's options areMusicPlayOptions.- Music plays on the element's own volume where it works, instead of always through Web Audio: a file from another origin no longer plays silent there.
0.5.2
Added
registerMaterial(name, { light, factors }): a game adds its own materials to the hue-shifted ramps, or changes a built-in one.Materialis the built-in set (BuiltinMaterial) or any registered name;MATERIALSandRAMP_FACTOR_SET(now exported) include what is registered.registerHandRamps(…): ramps picked by hand for a game's own effect colours.1681-engine/pixel/toneand1681-engine/pixel/paint: the pixel shapes and the colour/dither helpers as named exports (pixelPaintandpixelToneremain namespaces of1681-engine/pixel).selectiveOutline(g, width, dark)andgroundShadow(g, cx, cy, rx, ry, core, rim)take their colours.- A test that fails on a game's names or language anywhere in the engine, and "The engine knows no game" in the architecture guide.
Changed
pixelTone.ramphas no hand-picked ramps built in any more (they were one game's effect colours): every colour isderivedRampuntil a game registers its own.
0.5.1
Changed
- The library ships one file per source module (
dist/core/Stage.js,dist/pixel/pixel.js…) behind the same two entry points, and declares no side effects, so a game's bundler keeps only what it imports: a web worker that wants the colour helpers no longer takes Pixi, the audio engine and every other module with it.
0.5.0
Fixed
- The bundled audio worker (
takeCleanerWorker) is found again by a game's own bundler: 0.4.0 wrote its URL as an absolute/assets/…, so Vite failed to build any game importing the engine (Could not resolve entry module). The library is now built with a relative base and the worker sits beside the module that loads it.
0.4.0
Changed
- Node 22 or later (Node 20 reached its end of life; Pixi reads
navigator, which Node 20 lacks, as it loads).
Added
- A documentation site (VitePress): home, guide, an API reference generated from the source by TypeDoc, the changelog, and the examples running live;
npm run docs:dev/docs:build, published by a GitHub Pages workflow. - Examples
pixel-dungeon(dithered torchlight, a carried light, evaporating slimes) andecs-stress(tens of thousands of bodies with the frame rate and step time). - Types the public API refers to are exported too:
ComponentValues,ArrayOf,SettingsListener,Tween,TweenConfig,TweenTarget,EaseFn,Vec2,AtlasPlan,AtlasPage,CameraLike,SoundEngine,SoundOptions,TiledPropertyValue;PackedPixelsis exported as a class from the pixel module.
0.3.0
Added
- Characters:
RigViewdraws a rig from cut-out images or vector shapes (origin at the feet, mirrored byfacing, joints readable for held items);PosePlayerblends key poses with crossfades between clips;HUMANOID_POSES(72) andHUMANOID_CLIPS. LightLayer: smooth darkness with soft lights, colour washes and flicker.music/MusicPlayer: streamed tracks, crossfaded, looping, at the music volume, iOS-safe.- Multiplayer: the netcode protocol,
PredictedMover(prediction and reconciliation),InterpolationBuffer, an authoritativeRoomwith input validation and flood limits,RoomHostfor any Node WebSocket library, and a clientConnectionwith batching, clock sync and reconnection. Tested end to end with simulated latency and jitter, and through real sockets in the example. - Examples
night-garden(smooth art, rig, lights) andmultiplayer(npm run example:server).
0.2.0
The engine is now 1681 Engine (1681-engine), for 2D games in any art style; the pixel-art tools moved to their own module, Dither (1681-engine/pixel).
Breaking
- Pixel-art exports moved to
1681-engine/pixel:PixelLayer,pixelPaint,pixelTone, the colour and pixel-grid helpers,PALETTE, the prop kit,ramp/selectiveOutline,Darknessand the transient lights,Evaporation,spritePixels. stage.add.pixels()is gone: useaddPixelLayer(stage)from the pixel module, orstage.add.existing(obj)for any object made elsewhere.EngineOptions.pixelArt(default on) is nowpixelPerfect(default off). Off, textures are smooth, antialiased and drawn at the screen's density; on, they are sharp, at one backing pixel per CSS pixel, and the camera lands on whole pixels (Camera.roundPixels).createCollisionGrid's fifth argument is an options object (ground,shotOverTiles,bodyHalfWidth,bodyHalfHeight);setShotOverTilesis gone.
Added
AssetLoader: images, sprite sheets, JSON, text and Tiled maps with their tileset images, progress, a concurrency limit, errors that name the URL.- Tiled maps:
parseTiled(CSV and base64 layers, groups, objects, properties, flips, tile animations; clear refusals for what is not supported),collisionFromTiled(a layer or a tile property),TilemapView(layers drawn in culled chunks, each at its own depth among the bodies). Animator(fps or per-frame durations, loop or finish, speed) andanimationSystemwithSpriteComponentandAnimationComponent.Actions: named input bound to keys, gamepad buttons and sticks (with a dead zone), and virtual touch buttons; per-step edges, analog values, rebinding.Camera.track: a soft follow that is the same at any frame rate, a deadzone, an offset, the bounds kept.- Example
top-downwith a Tiled map, written byexamples/top-down/make-assets.mjs. world.each(A, B, fn): walks without allocating a row per entity.defineFields(name, { x: 'f32', … }): numeric components kept in typed-array columns (world.columns).world.slots(A, B): the matching slot indices as a cachedInt32Array, rebuilt only when membership changes. With field components a step over 50,000 bodies takes 0.1 ms, 200 times the first release.world.count(type),entityIndex(e),npm run bench.
Changed
Worldstores components in sparse sets instead of maps: a query over 50,000 bodies went from 21 ms to 2 ms a step, and spawning and destroying makes a third of the garbage. Entity ids are recycled with a generation, so a stale id never matches the entity that reuses its slot. Entities are walked last-added first.- Removing a component while a walk is in progress is deferred like destroying an entity, so nothing is skipped.
0.1.0 (as Dither Engine)
First release, split out of a shipped game's client.
Added
Engine,Scene,SceneManager(replace, push, pop, asynconEnter) andFixedLoop(fixed-step simulation with interpolation and a cap on catch-up steps).WorldECS: typed components, queries,fixedandframesystems, deferred destruction, error isolation.EventBuswithonce,off,listenerCountand listener error isolation.- Rendering core:
Stage,Camera,PixelLayer, display objects, tweens, timers, bitmap labels, texture atlas. - Pixel effects:
pixelPaint,pixelTone,Evaporation, hit-stop and recoil curves. - Lighting:
Darknesswith dithered lamps and transient lights. - Skeleton rig, pose blending, material ramps, selective outline and
HUMANOID_RIG. - Audio:
sound, synthesized recipes (registerSounds), recorded takes (registerTakes). - Input:
Keyboard, stick maths, long press, touch detection. - Networking: deterministic
stepMoverwith a configurable body box, binary snapshot codec, cadence rules, server clock. - Example:
walk-room.