Repository navigation
Migration
No breaking changes. Code that worked around the old behaviour can drop the workaround.
| Before | 2.4.0 |
|---|---|
Nothing emitted collision_enter or collision_exit
|
Physics emits both on the kernel event bus with { a, b }
|
plugins.remove(name) left the plugin's systems registered |
Systems registered during init() are unregistered. A systems.unregister call after it is now a no-op |
preloadSounds before the audio context did not register its keys |
Keys are registered and load when the context starts |
dopaminejs-plugin-webgl-particles 1.1.2 changes one log line: without WebGL it no longer says a canvas fallback is used.
Types only. The engine declarations now match the runtime, so TypeScript code written against the old ones stops compiling at these points. That code was already reading undefined when run.
| Was declared | Now declared |
|---|---|
GameObject.position: Vector2, scale: Vector2
|
x, y, scale: { x, y }, parent
|
Collider.getBounds() as { x, y, width, height }
|
{ left, right, top, bottom } for a box, { x, y, radius } for a circle |
Physics.checkOverlap(source, targetTag): GameObject | null |
checkOverlap(source, targetTag?): Collider[] |
Also declared now: Collider.tag, ParticleConfig.angle, and ParticleConfig.color as a string or a list of strings.
The core changes are additive. Three behaviours changed that code may depend on.
| Before | Now |
|---|---|
recordGame accepted any name and any score |
Throws a TypeError for an empty, non-string or reserved name, a non-object result, or a score that is not a finite number |
| A negative score took XP away | It earns the base 10 XP |
addXP or recordGame before init() threw a null TypeError
|
Throws an error that says init() must be awaited |
| The XP bar filled by lifetime XP | It fills by progress inside the current level |
showNotification wrapped rare and legendary messages in combo text |
The message is shown as written |
New in 2.3.0: Dopamine.destroy(), SoundManager.destroy(), the streak_updated and save_failed events, promise-returning storage, maxParticles and respectReducedMotion on ParticleSystem.
Breaking. Needs dopaminejs 2.3.0 or newer.
Rotate your webhook secret. The 1.x "signature" was the secret itself, base64 encoded together with the payload, in a header and in the body. Anything that saw a request saw the secret.
| 1.x | 2.0.0 |
|---|---|
Body carried a signature field |
Body is { event, data, timestamp }
|
Signature was btoa(secret + ':' + payload)
|
Header X-Dopamine-Signature: sha256=<hex HMAC-SHA256 of the body>
|
LeaderboardPlugin had a placeholder URL and a built-in secret |
Sends nothing until LeaderboardPlugin.configure({ webhookUrl, secret, playerId })
|
BattlePassPlugin logged tiers to the console |
Emits battlepass:tier-unlocked on the kernel bus and on window
|
Update your receiver to verify the header against the raw body. See Plugin: Ecosystem.
Both plugins now need the reward system to publish on the kernel bus:
const rewards = new RewardSystem(new DataService(), { events: game.kernel.events });The bundle dopaminejs-plugins was unpublished from npm on 2026-01-01 and removed from the repo in 2.1.0. Install the package that owns each export.
Was imported from dopaminejs-plugins
|
Now install |
|---|---|
WebGLParticlePlugin, WebGLParticleSystem
|
dopaminejs-plugin-webgl-particles |
HowlerAudioPlugin |
dopaminejs-plugin-howler-audio |
getSoundPack, listSoundPacks, SoundPacks
|
dopaminejs-plugin-sound-packs |
BattlePassPlugin, LeaderboardPlugin, RewardMiddleware, WebhookIntegration
|
dopaminejs-plugin-ecosystem |
DebugOverlayPlugin |
dopaminejs-plugin-debug-overlay |
CustomPhysicsPlugin |
Not published. Copy it from examples/plugins/CustomPhysicsPlugin.js. |
Sound packs are tone definitions, not file URLs. Do not pass one as customSounds. See Plugin: Sound Packs.
v2 put a kernel under the engine. v1 code keeps running.
GlobalPhysics, GlobalInput and GlobalLoader still work and warn once on first use. They will be removed in a major version. Move to the kernel:
// v1
import { GlobalPhysics, GlobalInput } from 'dopaminejs';
GlobalPhysics.add(collider);
// v2, in a component
class Player extends Component {
update(dt) {
if (this.input.isKeyDown('ArrowLeft')) this.gameObject.x -= 200 * dt;
}
}
// v2, outside a component
game.kernel.physics.add(collider);A Collider added to a game object registers itself with the kernel's physics. You rarely call physics.add by hand.
new Game(), game.start() and game.setScene(scene) work as before. The kernel is at game.kernel.
import Dopamine from 'dopaminejs' still works. New code uses the named exports. See Dopamine Facade.
Older docs used some names the code does not have:
| Wrong | Right |
|---|---|
scene.addGameObject(obj) |
scene.add(obj) |
game.changeScene(scene) |
game.setScene(scene) |
gameObject.position.x |
gameObject.x |
new ParticleSystem(canvas) |
new ParticleSystem(config) |
events.on('LEVEL_UP', ...) |
events.on('level_up', ...) |
soundManager.setSoundPack(name) |
no such method |
Start
Progression
Engine
Packages
Reference