Drop a pixel pal onto any site
Pixel Buddy is a tiny animated character that wanders your page, perches on elements, shows messages, and fires your custom actions. Plain JavaScript — one script tag and you're done.
i Overview
The buddy renders a pixel-art sprite to a canvas and animates it along the bottom of the page.
It wanders to random spots, climbs onto elements you tag with data-buddy-perch,
shows speech bubbles, and runs actions you register by name in JavaScript.
No framework
Dependency-free vanilla JS. Works on any site — React, static HTML, WordPress, anything.
Declarative
Mark interactive spots with data-buddy-* attributes. No JS required for perch spots.
Safe by design
Actions are looked up by name — markup never executes code directly.
Open source
MIT licensed. Self-host pixelbuddy.js or use the CDN with a Pro account.
1 Add the script
One script tag before </body>. The bundle injects its own CSS, the buddy canvas, and the speech bubble — nothing else to paste.
Open source (self-hosted)
<!-- before </body> --> <script src="https://cdn.jsdelivr.net/gh/acepsoft/pixelbuddy/pixelbuddy.min.js"></script>
Pro / Enterprise (CDN)
<script src="https://pixelbuddy.app/pixelbuddy.pro.js?key=YOUR_KEY" defer></script>
Your site key is shown on the site detail page in the dashboard. The Pro script fetches your configuration automatically — no window.BUDDY_CONFIG needed.
2 Configure
Set window.BUDDY_CONFIG before the script tag. Every field is optional — sensible defaults apply.
Pro users manage config from the dashboard instead; BUDDY_CONFIG is used as a fallback if the fetch fails.
window.BUDDY_CONFIG = {
character: "trex", // "trex" | "robot" | "cat" | "dog"
color: "green", // green | blue | pink | orange | purple
size: 92, // height in px
speed: 72, // walk speed, px/sec
personality: "calm", // calm | bouncy | sluggish | jittery
walkTime: 3, // seconds of strolling before a message
msgTime: 5, // seconds a message stays visible
message: "Need any help?",
clickLabel: "Yes please!", // button label in the speech bubble
clickAction: "openChat", // action to run on button click (optional)
clickArg: null, // argument(s) passed to it (value or array)
perchLines: ["Comfy up here!", "Nice view!"], // phrases shown when it perches on an untagged element
sticky: false, // keep perch bubbles up through mouse-move/scroll (per-element data-buddy-sticky overrides)
};
clickAction — a name you registered with defineAction. Leave it unset and a click just dismisses the bubble.perchLines is the pool of phrases the buddy shows when it climbs onto an element that has no message of its own. Precedence: an element's own data-buddy-say wins first, then your perchLines array, then the built-in defaults. Works the same on the free and Pro scripts — set it in BUDDY_CONFIG.3 Mark perch spots
Add data-buddy-perch to any element and the buddy will walk to it, climb on top, and optionally show a message.
<button data-buddy-perch data-buddy-say="Ready when you are!" data-buddy-stay="6" data-buddy-sticky data-buddy-button="Start now" data-buddy-action="goSignup" data-buddy-arg="/signup"> Sign up </button>
| Attribute | What it does |
|---|---|
data-buddy-perch |
Marks a climb spot. Optional value sit (on top) or peek (poke out from behind). Default: auto. |
data-buddy-say |
Text shown in the speech bubble while perched here. |
data-buddy-stay |
Seconds to linger. Default 5. |
data-buddy-sticky |
Keeps the speech bubble visible when the visitor moves the mouse or scrolls. Overrides the global sticky setting for this spot — add it to force-on, or data-buddy-sticky="false" to opt out when the global is on. Without either, the bubble hides on mouse movement. |
data-buddy-button |
Label for the button shown inside the speech bubble. |
data-buddy-action |
Name of a registered action. Runs when the button is clicked. |
data-buddy-arg |
Argument passed to the action. JSON array spreads into multiple params. |
4 Define actions
There's one built-in action — navigate, which sends the browser to a URL you pass as the arg.
Every other action you register by name, and the buddy looks it up when needed.
An action can do anything — open a chat, a modal, add to cart.
// register once, after pixelbuddy.js has loaded Buddy.defineAction("openChat", (seed) => myChat.open(seed)); Buddy.defineAction("openModal", (id) => showModal(id)); Buddy.defineAction("goSignup", (path) => { location.href = path; });
Need to pass multiple params? Make data-buddy-arg a JSON array — each item becomes a separate argument:
Buddy.defineAction("addToCart", (sku, qty, color) => store.add(sku, qty, color)); <!-- arg is parsed as JSON, each item spread as a param --> <div data-buddy-perch data-buddy-action="addToCart" data-buddy-arg='["sku-1234", 2, "red"]'>…</div>
Buddy.run("openChat") or Buddy.run("addToCart", "sku-1234", 2, "red"). The JSON in data-buddy-arg is parsed as data only — never executed.{ } JS API reference
Everything is on the global window.Buddy object.
| Method | Description |
|---|---|
Buddy.defineAction(name, fn) |
Register a named action. fn(...args) receives params from data-buddy-arg. |
Buddy.run(name, ...args) |
Fire a registered action immediately with any params. |
Buddy.configure(cfg) |
Update the live buddy at runtime — any subset of BUDDY_CONFIG (character, color, personality, message…). |
Buddy.showSpeech(text, sticky?, opts?) |
Show a speech bubble. opts = { button, action } adds a button. |
Buddy.perchNow(opts) |
Climb an element now. opts = { selector, pose, xFrac, hold, fast } |
Buddy.roam() |
Stop current action and resume free wandering. |
Buddy.help(msg?) |
Show the help bubble on demand. |
Buddy.jump() |
Make the buddy hop. |
Buddy.state() |
Returns current state object { mode, phase, pose, … }. |
Programmatic perching
// walk the buddy onto a specific element and hold there Buddy.perchNow({ selector: ".pricing-card", pose: "sit", // "sit" | "peek" xFrac: 0.5, // position along the top (0–1) hold: true, // stay until something else happens fast: true // scurry over instead of strolling }); Buddy.help("Need a hand?"); Buddy.run("openChat"); Buddy.roam();
✦ Pro / remote config
With a Pro or Enterprise account, the CDN script fetches your buddy configuration from the Pixel Buddy API on every page load. Changes made in the dashboard go live within 60 seconds — no code changes, no redeployment.
What each paid plan includes
Both paid plans cover one domain and remove the attribution badge. Enterprise adds higher limits, A/B testing, and a longer analytics window.
| Capability | Pro — $2.99/mo | Enterprise — $39.99/mo |
|---|---|---|
| Hosted CDN script & visual dashboard | ✓ | ✓ |
| Remote config (no redeploy) | ✓ | ✓ |
| Domains | 1 | 1 |
| Buddies per site | 3 | 10 |
| URL conditional messages | ✓ | ✓ |
| Analytics retention | 90 days | 12 months |
| A/B testing | — | ✓ |
| Team members | 3 | 5 |
| Attribution badge | Removed | Removed |
| Support | Priority |
Conditional messages
Set URL rules in the dashboard to show a different message on specific pages.
Patterns are matched against window.location.pathname; first match wins.
Use * as a wildcard (/pricing*, /docs/*, * for all pages).
Each rule can also set its own perch phrases for that page — the same idea as
perchLines, but managed per-URL from the dashboard with no code changes.
Actions still live on your site
The dashboard lets you set an action name (e.g. openChat) on a buddy or URL rule.
You still need to register that action on your site with Buddy.defineAction() — the name is just a key that links the two.
// your site's JS — runs after the buddy script loads Buddy.defineAction("openChat", () => myChatWidget.open());
🔒 Security
data-buddy-action is only ever used as a key to look up a function you registered with defineAction.
If a name isn't registered it's silently ignored.
data-buddy-arg is parsed as JSON data only — it is never eval'd or interpreted as code.Because you write every action handler, you control exactly what's allowed and can validate arguments however you like inside the function.