Skip to main content
Developer guide

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)
};
Clicking the buddy (or the help bubble's button) runs 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>
Fire actions from your own JS. You can run any registered action anywhere on the page, passing as many params as you like — 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)
Domains11
Buddies per site310
URL conditional messages
Analytics retention90 days12 months
A/B testing
Team members35
Attribution badgeRemovedRemoved
SupportEmailPriority

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

Markup can never execute code. 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.