Hi. I'm Antonio. I'm 53. I build things out of pure passion.
I don't talk much. But when I do… BeeDialogue comes out.
From “dialogue” to a dialogue plugin
BeeEngine is a small 2D HTML5 Canvas engine I keep shaping in the open. Physics, scenes, sprites, tiles, audio buses, an inventory plugin, a locale plugin. Useful pieces. Quiet pieces.
What was missing for story-shaped games was obvious: a place for NPCs to say something without me inventing a new if tree every weekend.
So v2.11.0 adds BeeDialogue.
Not a chatbot. Not a voice synthesizer. A dialogue tree: speakers, lines, branching choices, straight-line advances. The kind of thing you wire to a blue rectangle that pretends to be a guide on a bridge.
The engine stays blind (on purpose)
BeeEngine has a plugin contract I take seriously. The core does not import plugins. A plugin attaches itself.
import { BeeEngine } from 'beeengine';
import { BeeDialogue, BeeLocale } from 'beeengine/plugins';
const game = new BeeEngine('c', 800, 480);
const locale = new BeeLocale({ /* strings… */, engine: game });
const dialogue = new BeeDialogue({ engine: game, locale, cancelKey: 'Escape' });
// attach() registers it as 'dialogue' — detach() unregisters
No monkey-patch of loop(). No hijacking of update / render. BeeDialogue has no tick of its own and no DOM listeners. Your scene calls the verbs:
-
handleInput(input)fromscene.update -
draw(ctx)fromscene.draw(screen space, after the camera)
The motor stays a motor. The plugin stays a plugin. I sleep better.
A tree you can read
You load a tree once. Nodes can be an array (easier to skim) or a map. Literal text / speaker work. Or textKey / speakerKey if you pass an optional BeeLocale (constructor, setLocale, or engine.plugin('locale')).
dialogue.load({
start: 'intro',
nodes: [
{
id: 'intro',
speakerKey: 'guide',
textKey: 'hi',
choices: [
{ textKey: 'ask_map', next: 'map', data: { topic: 'map' } },
{ textKey: 'bye', next: null, data: { topic: 'bye' } }
]
},
{ id: 'map', speakerKey: 'guide', textKey: 'map', next: 'tip' },
{ id: 'tip', speakerKey: 'guide', textKey: 'tip' }
]
});
Rules that saved me from myself:
- Node without
choices→advance()followsnext(or ends). - Node with
choices→choose(index). - The wrong verb returns
falseand warns. Good. I need warnings more than cleverness. -
start()while already open does a clean restart (endwith reasonrestart, thenstartagain).
Drawing choices (without becoming a second renderer)
BeeDialogue is not BeeText.drawHUD. It draws a simple panel when you ask.
const scene = {
update(_dt, input) {
if (input.wasPressed('KeyE') && !dialogue.isOpen()) dialogue.start();
dialogue.handleInput(input); // Space / Enter advances or confirms; arrows move the highlight
},
draw(ctx) {
dialogue.draw(ctx, { x: 40, width: ctx.canvas.width - 80 });
}
};
onChange reports start, node, choice, advance, and end (with a reason: finished, cancel, restart, reload, missing-node…). Enough to feed a future quest plugin without hard-wiring anything today.
And yes — toJSON / fromJSON save the tree and where you stood, so you can tuck the DTO into BeeSave next to inventory slots. Definitions stay yours; restore reopens on the saved node and emits node (not a fake second start).
About that demo label
The demo is examples/dialogue.html. Press E.
The English string does not say “E to talk”.
It says “E to interact”.
Because BeeDialogue does not talk. It does not clear its throat. It does not do regional accents. It advances nodes. The joke writes itself, and I left it in the locale table on purpose (talk: 'E to interact'). Press L if you want Italian. The guide still will not speak aloud. She will, however, mention a village east of a bridge, which is already more social than most of my week.
Why this shape
I like small contracts:
| Piece | Job |
|---|---|
| BeeLocale |
t(key) — pure read |
| BeeInventory | stacks, use, equip — no draw loop |
| BeeDialogue | tree state — you draw and feed input |
Together they feel like a tiny RPG toolkit without pretending to be one. Next ideas on the shelf (one at a time): Quest, Post FX, scene Transition, action remapping… Lights, Spine, Net later. Those touch more surface area. Dialogue was the polite guest who hangs its coat on attach and leaves with detach.
Try it
- Repo: BeeEngine-V2.5.0 (package
beeengine2.11.0) - Demo:
examples/dialogue.html - Tests:
node scripts/test-beedialogue.mjs - Import:
import { BeeDialogue } from 'beeengine/plugins'
If you break it, tell me how. If you use it in a jam game with three lines of dialogue and a torch tip, tell me that too. Short messages welcome — I don't talk much.
But when I do… you already know what comes out.
— Antonio
Top comments (0)