Run from source, build an installer, and check visual changes on a project whose output is GPU-composited and cannot be screenshotted.
Requires git and Node 20 or newer.
git clone https://github.com/JOhnsonKC201/pixelpets.git
cd pixelpets
npm install
npm startnpm start # run the app
npm test # 200+ tests: config and data migration, poses, interactions,
# audio, focus and quiet hours, mail and calendar, site drift
npm run lint # eslint over src, tests and scripts (what CI runs)
npm run test:boot # launch the real app and assert it renders a frame
npm run check:hook # does the global input hook load on this Electron?
npm run poses:cat # previews/cat-poses.png (every activity x every coat)
npm run poses:dog # the same for the Black Lab
npm run frames:import -- <dir> # import painted PNGs as baked poses
npm run demo:all # regenerate the README media (hero, gallery, carousel)
npm run hook -- cursor # print a path-filled agent hook config
npm run icon # regenerate the tray + app-tile iconsCI runs npm run lint, npm test, and the boot check on Windows and macOS for
every push and pull request.
Tip
Windows: npm start leaves a console window open behind the pet.
launch-pixelpets.vbs starts the same thing silently, so it works as a desktop
shortcut (point one at wscript.exe "<path>\launch-pixelpets.vbs"). It locates
the project from its own path, so moving the folder does not break it.
Note
macOS: same commands. On first run, grant Accessibility permission (System Settings > Privacy & Security > Accessibility) so your pet can react to your typing and cursor. Input is only detected, never logged or sent anywhere.
npm run pack # portable build -> dist/win-unpacked/pixelpets.exe (no installer)
npm run dist # Windows installer -> dist/pixelpets Setup <version>.exepack works out of the box. dist (the NSIS installer) needs permission to
create symlinks while electron-builder unpacks its bundled signing tools: enable
Windows Developer Mode (Settings > Privacy & security > For developers) or run
the build once from an Administrator terminal. The native uiohook-napi module
ships N-API prebuilds, so npmRebuild is disabled in the build config and no
Visual Studio is needed.
macOS: npm run dist:mac (on a Mac) builds a dmg and zip for Apple Silicon
and Intel; the release workflow also builds them in CI on every version tag.
Builds are ad-hoc signed rather than notarized, so Gatekeeper still asks before
the first launch: open it once, then System Settings > Privacy & Security >
Open Anyway. The ad-hoc seal matters for more than the warning: macOS keys the
Accessibility grant and the Keychain entry to a code signature, so without it both
would be thrown away on every update.
The mac port is code-complete but has not been smoke-tested on real hardware. If you own a Mac, running this and opening an issue with whatever you see is the single most useful contribution to the project right now.
- The overlay shows over all apps and Spaces, including fullscreen.
- Clicks pass through everywhere except on the pet.
- The typing reaction works after granting Accessibility.
- The pet rests on the Dock edge, not the menu bar.
- The tray menu works in the menu bar.
- Login launch works.
The overlay is GPU-composited, so ordinary screenshots cannot capture it. Poses are reviewed with a one-command contact sheet instead:
npm run poses:catThat renders every activity across every coat into one image, bottom-aligned
on a shared floor line so silhouettes can be compared down a column. It runs
headlessly, with no Electron and no GPU, by loading the overlay's script stack in
a vm (scripts/overlay-vm.js) and reading the pose grids back out. That is also
what the pose tests drive, so the sheet and the suite are looking at exactly the
same sprites.
Single poses preview via:
npx electron . --shot --state=<sit|typing|hunt|loaf|groom|paper|overheat|pet|startle|work|rearup> --pattern=<coat>Add --at=<ms> to capture an animated pose at a chosen phase (a typing key-press,
say): npx electron . --shot --state=typing --at=760. Add --note="<text>" to
pin a speech bubble open in the capture, which is how bubble wrapping and
screen-edge clamping get checked against a real font rather than only in unit
tests: npx electron . --shot --note="a long reminder that has to wrap".
The butterfly has two flags of its own. --bfly forces a visit from the first
frame, so npx electron . --shot --bfly --state=rearup --at=1021 frames the
rear-up swat with the bug in shot. The catch itself cannot happen in a shot (both
pounce triggers stand down under --shot), so --joy pins the victory beat that
follows one: happy eyes and a blush the whole time, and the double bounce looping
every two seconds (1.4 s of bounce, 0.6 s still), the way --state=done loops its
hop. It loops because --at counts from page load while the renderer's clock
started earlier, so no single --at lands on a known phase: take two or three
captures a few hundred milliseconds apart and one of them is mid-bounce. The
"oh!" beat when the bug is first spotted opens on the very first frame, before
the capture timer starts, so it is covered by a test rather than a shot.
Every pose is composed in code, which is why one sit covers 15 coats and a new
coat costs nine hex values instead of an art pass. The trade is that changing how
the pet looks means editing geometry.
npm run frames:import is the escape hatch: paint a pose against a placeholder
palette, import it, and it wins over the composer for exactly the coats you name
while everything else keeps composing. Five held poses can be baked (sit,
type, loaf, rear, hunt); the six raised-limb activities are parameterised
rigs whose limbs sweep through quantised frames, so a still would freeze them.
Palette, naming and the checks the importer runs are in frame-pack.md.
The hero banner, the gallery, and the coat carousel are all rendered from the
same sprite geometry by scripts/make-demo-gif.js, in pure Node with no browser
or GPU. Regenerate any one with
node scripts/make-demo-gif.js <hero|gallery|carousel> [mp4], or all three with
npm run demo:all.
Electron, HTML canvas, Web Audio,
uiohook-napi (system-wide keyboard
hook), imapflow, and
node-ical.