diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml
index 078199393b0..6df0ec6f6b1 100644
--- a/.github/workflows/nodejs.yml
+++ b/.github/workflows/nodejs.yml
@@ -25,7 +25,7 @@ jobs:
fetch-depth: 0
- name: Derive SHAs for nx affected
- uses: nrwl/nx-set-shas@v4
+ uses: nrwl/nx-set-shas@v5
- uses: actions/setup-node@v6
with:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7e888a72c51..bab98c7aabd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [4.0.0-beta.1](https://github.com/tsparticles/tsparticles/compare/v4.0.0-beta.0...v4.0.0-beta.1) (2026-03-31)
+
+### Bug Fixes
+
+- improved cannon and interactivity events ([b836a59](https://github.com/tsparticles/tsparticles/commit/b836a59e90afdc68b6307e8d37898e71b5881a21))
+- improved cannon options loading ([8cfcc50](https://github.com/tsparticles/tsparticles/commit/8cfcc50f4375582b73ff79569a097aea8464c8c7))
+- improved cannon vector draw ([5d55e8f](https://github.com/tsparticles/tsparticles/commit/5d55e8f22aa34939ac96a58ef9e1eb478f33e9f6))
+
+### Features
+
+- added cannon external interaction ([e133ab3](https://github.com/tsparticles/tsparticles/commit/e133ab35835b6411c0da1b151b046a36bcfd9ee1))
+
# [4.0.0-beta.0](https://github.com/tsparticles/tsparticles/compare/v4.0.0-alpha.28...v4.0.0-beta.0) (2026-03-19)
### Bug Fixes
diff --git a/README.md b/README.md
index 6de2e0efbdf..22a80c16f30 100644
--- a/README.md
+++ b/README.md
@@ -98,8 +98,6 @@ React.js, Vue.js (2.x and 3.x), Angular, Svelte, jQuery, Preact, Inferno, Riot.j
- [Migrating from Particles.js](#migrating-from-particlesjs)
- [Plugins/Customizations](#pluginscustomizations)
- [Dependency Graph](#dependency-graph)
- - [Sponsors](#sponsors)
- - [Huge thanks to JetBrains for the 2020-2022 Open Source Licenses!](#huge-thanks-to-jetbrains-for-the-2020-2022-open-source-licenses)
---
@@ -989,22 +987,3 @@ flowchart TD
bundle-full --> bundle-all
```
-
----
-
-
-
-
-
-
-
-
-
-
-## Sponsors
-
-### JetBrains
-
-Huge thanks to [JetBrains](https://www.jetbrains.com/?from=tsParticles) for the 2020-2022 Open Source Licenses!
-
-[JetBrains WebStorm](https://www.jetbrains.com/webstorm/?from=tsParticles) is used to maintain this project.
diff --git a/bundles/all/CHANGELOG.md b/bundles/all/CHANGELOG.md
index 3f2c507d484..4ce68f2c01f 100644
--- a/bundles/all/CHANGELOG.md
+++ b/bundles/all/CHANGELOG.md
@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [4.0.0-beta.1](https://github.com/tsparticles/tsparticles/compare/v4.0.0-beta.0...v4.0.0-beta.1) (2026-03-31)
+
+### Features
+
+- added cannon external interaction ([e133ab3](https://github.com/tsparticles/tsparticles/commit/e133ab35835b6411c0da1b151b046a36bcfd9ee1))
+
# [4.0.0-beta.0](https://github.com/tsparticles/tsparticles/compare/v4.0.0-alpha.28...v4.0.0-beta.0) (2026-03-19)
**Note:** Version bump only for package @tsparticles/all
diff --git a/bundles/all/package.dist.json b/bundles/all/package.dist.json
index 97266920351..1b78af01206 100644
--- a/bundles/all/package.dist.json
+++ b/bundles/all/package.dist.json
@@ -1,6 +1,6 @@
{
"name": "@tsparticles/all",
- "version": "4.0.0-beta.0",
+ "version": "4.0.0-beta.1",
"description": "Easily create highly customizable particle animations and use them as animated backgrounds for your website. Ready to use components available also for React, Vue.js (2.x and 3.x), Angular, Svelte, jQuery, Preact, Riot.js, Inferno.",
"homepage": "https://particles.js.org",
"repository": {
@@ -99,82 +99,83 @@
"./package.json": "./package.json"
},
"dependencies": {
- "@tsparticles/effect-bubble": "4.0.0-beta.0",
- "@tsparticles/effect-particles": "4.0.0-beta.0",
- "@tsparticles/effect-shadow": "4.0.0-beta.0",
- "@tsparticles/effect-trail": "4.0.0-beta.0",
- "@tsparticles/engine": "4.0.0-beta.0",
- "@tsparticles/interaction-external-particle": "4.0.0-beta.0",
- "@tsparticles/interaction-external-pop": "4.0.0-beta.0",
- "@tsparticles/interaction-light": "4.0.0-beta.0",
- "@tsparticles/interaction-particles-repulse": "4.0.0-beta.0",
- "@tsparticles/path-branches": "4.0.0-beta.0",
- "@tsparticles/path-brownian": "4.0.0-beta.0",
- "@tsparticles/path-curl-noise": "4.0.0-beta.0",
- "@tsparticles/path-curves": "4.0.0-beta.0",
- "@tsparticles/path-fractal-noise": "4.0.0-beta.0",
- "@tsparticles/path-grid": "4.0.0-beta.0",
- "@tsparticles/path-levy": "4.0.0-beta.0",
- "@tsparticles/path-perlin-noise": "4.0.0-beta.0",
- "@tsparticles/path-polygon": "4.0.0-beta.0",
- "@tsparticles/path-random": "4.0.0-beta.0",
- "@tsparticles/path-simplex-noise": "4.0.0-beta.0",
- "@tsparticles/path-spiral": "4.0.0-beta.0",
- "@tsparticles/path-svg": "4.0.0-beta.0",
- "@tsparticles/path-zig-zag": "4.0.0-beta.0",
- "@tsparticles/plugin-background-mask": "4.0.0-beta.0",
- "@tsparticles/plugin-blend": "4.0.0-beta.0",
- "@tsparticles/plugin-canvas-mask": "4.0.0-beta.0",
- "@tsparticles/plugin-easing-back": "4.0.0-beta.0",
- "@tsparticles/plugin-easing-bounce": "4.0.0-beta.0",
- "@tsparticles/plugin-easing-circ": "4.0.0-beta.0",
- "@tsparticles/plugin-easing-cubic": "4.0.0-beta.0",
- "@tsparticles/plugin-easing-elastic": "4.0.0-beta.0",
- "@tsparticles/plugin-easing-expo": "4.0.0-beta.0",
- "@tsparticles/plugin-easing-gaussian": "4.0.0-beta.0",
- "@tsparticles/plugin-easing-linear": "4.0.0-beta.0",
- "@tsparticles/plugin-easing-quart": "4.0.0-beta.0",
- "@tsparticles/plugin-easing-quint": "4.0.0-beta.0",
- "@tsparticles/plugin-easing-sigmoid": "4.0.0-beta.0",
- "@tsparticles/plugin-easing-sine": "4.0.0-beta.0",
- "@tsparticles/plugin-easing-smoothstep": "4.0.0-beta.0",
- "@tsparticles/plugin-emitters-shape-canvas": "4.0.0-beta.0",
- "@tsparticles/plugin-emitters-shape-path": "4.0.0-beta.0",
- "@tsparticles/plugin-emitters-shape-polygon": "4.0.0-beta.0",
- "@tsparticles/plugin-export-image": "4.0.0-beta.0",
- "@tsparticles/plugin-export-json": "4.0.0-beta.0",
- "@tsparticles/plugin-export-video": "4.0.0-beta.0",
- "@tsparticles/plugin-hsv-color": "4.0.0-beta.0",
- "@tsparticles/plugin-hwb-color": "4.0.0-beta.0",
- "@tsparticles/plugin-infection": "4.0.0-beta.0",
- "@tsparticles/plugin-lab-color": "4.0.0-beta.0",
- "@tsparticles/plugin-lch-color": "4.0.0-beta.0",
- "@tsparticles/plugin-manual-particles": "4.0.0-beta.0",
- "@tsparticles/plugin-motion": "4.0.0-beta.0",
- "@tsparticles/plugin-named-color": "4.0.0-beta.0",
- "@tsparticles/plugin-oklab-color": "4.0.0-beta.0",
- "@tsparticles/plugin-oklch-color": "4.0.0-beta.0",
- "@tsparticles/plugin-poisson-disc": "4.0.0-beta.0",
- "@tsparticles/plugin-polygon-mask": "4.0.0-beta.0",
- "@tsparticles/plugin-responsive": "4.0.0-beta.0",
- "@tsparticles/plugin-sounds": "4.0.0-beta.0",
- "@tsparticles/plugin-themes": "4.0.0-beta.0",
- "@tsparticles/plugin-trail": "4.0.0-beta.0",
- "@tsparticles/plugin-zoom": "4.0.0-beta.0",
- "@tsparticles/shape-arrow": "4.0.0-beta.0",
- "@tsparticles/shape-cards": "4.0.0-beta.0",
- "@tsparticles/shape-cog": "4.0.0-beta.0",
- "@tsparticles/shape-heart": "4.0.0-beta.0",
- "@tsparticles/shape-infinity": "4.0.0-beta.0",
- "@tsparticles/shape-matrix": "4.0.0-beta.0",
- "@tsparticles/shape-path": "4.0.0-beta.0",
- "@tsparticles/shape-rounded-polygon": "4.0.0-beta.0",
- "@tsparticles/shape-rounded-rect": "4.0.0-beta.0",
- "@tsparticles/shape-spiral": "4.0.0-beta.0",
- "@tsparticles/shape-squircle": "4.0.0-beta.0",
- "@tsparticles/updater-gradient": "4.0.0-beta.0",
- "@tsparticles/updater-orbit": "4.0.0-beta.0",
- "tsparticles": "4.0.0-beta.0"
+ "@tsparticles/effect-bubble": "4.0.0-beta.1",
+ "@tsparticles/effect-particles": "4.0.0-beta.1",
+ "@tsparticles/effect-shadow": "4.0.0-beta.1",
+ "@tsparticles/effect-trail": "4.0.0-beta.1",
+ "@tsparticles/engine": "4.0.0-beta.1",
+ "@tsparticles/interaction-external-cannon": "4.0.0-beta.1",
+ "@tsparticles/interaction-external-particle": "4.0.0-beta.1",
+ "@tsparticles/interaction-external-pop": "4.0.0-beta.1",
+ "@tsparticles/interaction-light": "4.0.0-beta.1",
+ "@tsparticles/interaction-particles-repulse": "4.0.0-beta.1",
+ "@tsparticles/path-branches": "4.0.0-beta.1",
+ "@tsparticles/path-brownian": "4.0.0-beta.1",
+ "@tsparticles/path-curl-noise": "4.0.0-beta.1",
+ "@tsparticles/path-curves": "4.0.0-beta.1",
+ "@tsparticles/path-fractal-noise": "4.0.0-beta.1",
+ "@tsparticles/path-grid": "4.0.0-beta.1",
+ "@tsparticles/path-levy": "4.0.0-beta.1",
+ "@tsparticles/path-perlin-noise": "4.0.0-beta.1",
+ "@tsparticles/path-polygon": "4.0.0-beta.1",
+ "@tsparticles/path-random": "4.0.0-beta.1",
+ "@tsparticles/path-simplex-noise": "4.0.0-beta.1",
+ "@tsparticles/path-spiral": "4.0.0-beta.1",
+ "@tsparticles/path-svg": "4.0.0-beta.1",
+ "@tsparticles/path-zig-zag": "4.0.0-beta.1",
+ "@tsparticles/plugin-background-mask": "4.0.0-beta.1",
+ "@tsparticles/plugin-blend": "4.0.0-beta.1",
+ "@tsparticles/plugin-canvas-mask": "4.0.0-beta.1",
+ "@tsparticles/plugin-easing-back": "4.0.0-beta.1",
+ "@tsparticles/plugin-easing-bounce": "4.0.0-beta.1",
+ "@tsparticles/plugin-easing-circ": "4.0.0-beta.1",
+ "@tsparticles/plugin-easing-cubic": "4.0.0-beta.1",
+ "@tsparticles/plugin-easing-elastic": "4.0.0-beta.1",
+ "@tsparticles/plugin-easing-expo": "4.0.0-beta.1",
+ "@tsparticles/plugin-easing-gaussian": "4.0.0-beta.1",
+ "@tsparticles/plugin-easing-linear": "4.0.0-beta.1",
+ "@tsparticles/plugin-easing-quart": "4.0.0-beta.1",
+ "@tsparticles/plugin-easing-quint": "4.0.0-beta.1",
+ "@tsparticles/plugin-easing-sigmoid": "4.0.0-beta.1",
+ "@tsparticles/plugin-easing-sine": "4.0.0-beta.1",
+ "@tsparticles/plugin-easing-smoothstep": "4.0.0-beta.1",
+ "@tsparticles/plugin-emitters-shape-canvas": "4.0.0-beta.1",
+ "@tsparticles/plugin-emitters-shape-path": "4.0.0-beta.1",
+ "@tsparticles/plugin-emitters-shape-polygon": "4.0.0-beta.1",
+ "@tsparticles/plugin-export-image": "4.0.0-beta.1",
+ "@tsparticles/plugin-export-json": "4.0.0-beta.1",
+ "@tsparticles/plugin-export-video": "4.0.0-beta.1",
+ "@tsparticles/plugin-hsv-color": "4.0.0-beta.1",
+ "@tsparticles/plugin-hwb-color": "4.0.0-beta.1",
+ "@tsparticles/plugin-infection": "4.0.0-beta.1",
+ "@tsparticles/plugin-lab-color": "4.0.0-beta.1",
+ "@tsparticles/plugin-lch-color": "4.0.0-beta.1",
+ "@tsparticles/plugin-manual-particles": "4.0.0-beta.1",
+ "@tsparticles/plugin-motion": "4.0.0-beta.1",
+ "@tsparticles/plugin-named-color": "4.0.0-beta.1",
+ "@tsparticles/plugin-oklab-color": "4.0.0-beta.1",
+ "@tsparticles/plugin-oklch-color": "4.0.0-beta.1",
+ "@tsparticles/plugin-poisson-disc": "4.0.0-beta.1",
+ "@tsparticles/plugin-polygon-mask": "4.0.0-beta.1",
+ "@tsparticles/plugin-responsive": "4.0.0-beta.1",
+ "@tsparticles/plugin-sounds": "4.0.0-beta.1",
+ "@tsparticles/plugin-themes": "4.0.0-beta.1",
+ "@tsparticles/plugin-trail": "4.0.0-beta.1",
+ "@tsparticles/plugin-zoom": "4.0.0-beta.1",
+ "@tsparticles/shape-arrow": "4.0.0-beta.1",
+ "@tsparticles/shape-cards": "4.0.0-beta.1",
+ "@tsparticles/shape-cog": "4.0.0-beta.1",
+ "@tsparticles/shape-heart": "4.0.0-beta.1",
+ "@tsparticles/shape-infinity": "4.0.0-beta.1",
+ "@tsparticles/shape-matrix": "4.0.0-beta.1",
+ "@tsparticles/shape-path": "4.0.0-beta.1",
+ "@tsparticles/shape-rounded-polygon": "4.0.0-beta.1",
+ "@tsparticles/shape-rounded-rect": "4.0.0-beta.1",
+ "@tsparticles/shape-spiral": "4.0.0-beta.1",
+ "@tsparticles/shape-squircle": "4.0.0-beta.1",
+ "@tsparticles/updater-gradient": "4.0.0-beta.1",
+ "@tsparticles/updater-orbit": "4.0.0-beta.1",
+ "tsparticles": "4.0.0-beta.1"
},
"publishConfig": {
"access": "public"
diff --git a/bundles/all/package.json b/bundles/all/package.json
index 86a4db7feaa..8d5ba91b45c 100644
--- a/bundles/all/package.json
+++ b/bundles/all/package.json
@@ -1,6 +1,6 @@
{
"name": "@tsparticles/all",
- "version": "4.0.0-beta.0",
+ "version": "4.0.0-beta.1",
"description": "Easily create highly customizable particle animations and use them as animated backgrounds for your website. Ready to use components available also for React, Vue.js (2.x and 3.x), Angular, Svelte, jQuery, Preact, Riot.js, Inferno.",
"homepage": "https://particles.js.org",
"scripts": {
@@ -107,82 +107,83 @@
"./package.json": "./dist/package.json"
},
"dependencies": {
- "@tsparticles/effect-bubble": "workspace:4.0.0-beta.0",
- "@tsparticles/effect-particles": "workspace:4.0.0-beta.0",
- "@tsparticles/effect-shadow": "workspace:4.0.0-beta.0",
- "@tsparticles/effect-trail": "workspace:4.0.0-beta.0",
- "@tsparticles/engine": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-external-particle": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-external-pop": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-light": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-particles-repulse": "workspace:4.0.0-beta.0",
- "@tsparticles/path-branches": "workspace:4.0.0-beta.0",
- "@tsparticles/path-brownian": "workspace:4.0.0-beta.0",
- "@tsparticles/path-curl-noise": "workspace:4.0.0-beta.0",
- "@tsparticles/path-curves": "workspace:4.0.0-beta.0",
- "@tsparticles/path-fractal-noise": "workspace:4.0.0-beta.0",
- "@tsparticles/path-grid": "workspace:4.0.0-beta.0",
- "@tsparticles/path-levy": "workspace:4.0.0-beta.0",
- "@tsparticles/path-perlin-noise": "workspace:4.0.0-beta.0",
- "@tsparticles/path-polygon": "workspace:4.0.0-beta.0",
- "@tsparticles/path-random": "workspace:4.0.0-beta.0",
- "@tsparticles/path-simplex-noise": "workspace:4.0.0-beta.0",
- "@tsparticles/path-spiral": "workspace:4.0.0-beta.0",
- "@tsparticles/path-svg": "workspace:4.0.0-beta.0",
- "@tsparticles/path-zig-zag": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-background-mask": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-blend": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-canvas-mask": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-easing-back": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-easing-bounce": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-easing-circ": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-easing-cubic": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-easing-elastic": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-easing-expo": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-easing-gaussian": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-easing-linear": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-easing-quart": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-easing-quint": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-easing-sigmoid": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-easing-sine": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-easing-smoothstep": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-emitters-shape-canvas": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-emitters-shape-path": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-emitters-shape-polygon": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-export-image": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-export-json": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-export-video": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-hsv-color": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-hwb-color": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-infection": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-lab-color": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-lch-color": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-manual-particles": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-motion": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-named-color": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-oklab-color": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-oklch-color": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-poisson-disc": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-polygon-mask": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-responsive": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-sounds": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-themes": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-trail": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-zoom": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-arrow": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-cards": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-cog": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-heart": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-infinity": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-matrix": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-path": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-rounded-polygon": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-rounded-rect": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-spiral": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-squircle": "workspace:4.0.0-beta.0",
- "@tsparticles/updater-gradient": "workspace:4.0.0-beta.0",
- "@tsparticles/updater-orbit": "workspace:4.0.0-beta.0",
- "tsparticles": "workspace:4.0.0-beta.0"
+ "@tsparticles/effect-bubble": "workspace:4.0.0-beta.1",
+ "@tsparticles/effect-particles": "workspace:4.0.0-beta.1",
+ "@tsparticles/effect-shadow": "workspace:4.0.0-beta.1",
+ "@tsparticles/effect-trail": "workspace:4.0.0-beta.1",
+ "@tsparticles/engine": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-external-cannon": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-external-particle": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-external-pop": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-light": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-particles-repulse": "workspace:4.0.0-beta.1",
+ "@tsparticles/path-branches": "workspace:4.0.0-beta.1",
+ "@tsparticles/path-brownian": "workspace:4.0.0-beta.1",
+ "@tsparticles/path-curl-noise": "workspace:4.0.0-beta.1",
+ "@tsparticles/path-curves": "workspace:4.0.0-beta.1",
+ "@tsparticles/path-fractal-noise": "workspace:4.0.0-beta.1",
+ "@tsparticles/path-grid": "workspace:4.0.0-beta.1",
+ "@tsparticles/path-levy": "workspace:4.0.0-beta.1",
+ "@tsparticles/path-perlin-noise": "workspace:4.0.0-beta.1",
+ "@tsparticles/path-polygon": "workspace:4.0.0-beta.1",
+ "@tsparticles/path-random": "workspace:4.0.0-beta.1",
+ "@tsparticles/path-simplex-noise": "workspace:4.0.0-beta.1",
+ "@tsparticles/path-spiral": "workspace:4.0.0-beta.1",
+ "@tsparticles/path-svg": "workspace:4.0.0-beta.1",
+ "@tsparticles/path-zig-zag": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-background-mask": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-blend": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-canvas-mask": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-easing-back": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-easing-bounce": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-easing-circ": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-easing-cubic": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-easing-elastic": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-easing-expo": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-easing-gaussian": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-easing-linear": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-easing-quart": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-easing-quint": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-easing-sigmoid": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-easing-sine": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-easing-smoothstep": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-emitters-shape-canvas": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-emitters-shape-path": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-emitters-shape-polygon": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-export-image": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-export-json": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-export-video": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-hsv-color": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-hwb-color": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-infection": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-lab-color": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-lch-color": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-manual-particles": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-motion": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-named-color": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-oklab-color": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-oklch-color": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-poisson-disc": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-polygon-mask": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-responsive": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-sounds": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-themes": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-trail": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-zoom": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-arrow": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-cards": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-cog": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-heart": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-infinity": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-matrix": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-path": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-rounded-polygon": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-rounded-rect": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-spiral": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-squircle": "workspace:4.0.0-beta.1",
+ "@tsparticles/updater-gradient": "workspace:4.0.0-beta.1",
+ "@tsparticles/updater-orbit": "workspace:4.0.0-beta.1",
+ "tsparticles": "workspace:4.0.0-beta.1"
},
"publishConfig": {
"access": "public",
diff --git a/bundles/all/src/index.ts b/bundles/all/src/index.ts
index 4bc18ca4cce..0c4cd8af1ef 100644
--- a/bundles/all/src/index.ts
+++ b/bundles/all/src/index.ts
@@ -13,7 +13,7 @@ declare const __VERSION__: string;
export async function loadAll(engine: Engine): Promise {
engine.checkVersion(__VERSION__);
- await engine.register(async e => {
+ await engine.pluginManager.register(async e => {
const [
{ loadFull },
@@ -57,6 +57,7 @@ export async function loadAll(engine: Engine): Promise {
{ loadExportJSONPlugin },
{ loadExportVideoPlugin },
+ { loadExternalCannonInteraction },
{ loadExternalParticleInteraction },
{ loadExternalPopInteraction },
{ loadLightInteraction },
@@ -143,6 +144,7 @@ export async function loadAll(engine: Engine): Promise {
import("@tsparticles/plugin-export-json"),
import("@tsparticles/plugin-export-video"),
+ import("@tsparticles/interaction-external-cannon"),
import("@tsparticles/interaction-external-particle"),
import("@tsparticles/interaction-external-pop"),
import("@tsparticles/interaction-light"),
@@ -193,6 +195,7 @@ export async function loadAll(engine: Engine): Promise {
await loadFull(e);
await Promise.all([
+ loadExternalCannonInteraction(e),
loadExternalParticleInteraction(e),
loadExternalPopInteraction(e),
loadLightInteraction(e),
diff --git a/bundles/basic/CHANGELOG.md b/bundles/basic/CHANGELOG.md
index 627476e78ca..1142307a271 100644
--- a/bundles/basic/CHANGELOG.md
+++ b/bundles/basic/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [4.0.0-beta.1](https://github.com/tsparticles/tsparticles/compare/v4.0.0-beta.0...v4.0.0-beta.1) (2026-03-31)
+
+**Note:** Version bump only for package @tsparticles/basic
+
# [4.0.0-beta.0](https://github.com/tsparticles/tsparticles/compare/v4.0.0-alpha.28...v4.0.0-beta.0) (2026-03-19)
**Note:** Version bump only for package @tsparticles/basic
diff --git a/bundles/basic/package.dist.json b/bundles/basic/package.dist.json
index 58863c4ad4d..c881d72ef35 100644
--- a/bundles/basic/package.dist.json
+++ b/bundles/basic/package.dist.json
@@ -1,6 +1,6 @@
{
"name": "@tsparticles/basic",
- "version": "4.0.0-beta.0",
+ "version": "4.0.0-beta.1",
"description": "Easily create highly customizable particle animations and use them as animated backgrounds for your website. Ready to use components available also for React, Vue.js (2.x and 3.x), Angular, Svelte, jQuery, Preact, Riot.js, Inferno.",
"homepage": "https://particles.js.org",
"repository": {
@@ -99,16 +99,16 @@
"./package.json": "./package.json"
},
"dependencies": {
- "@tsparticles/engine": "4.0.0-beta.0",
- "@tsparticles/plugin-hex-color": "4.0.0-beta.0",
- "@tsparticles/plugin-hsl-color": "4.0.0-beta.0",
- "@tsparticles/plugin-move": "4.0.0-beta.0",
- "@tsparticles/plugin-rgb-color": "4.0.0-beta.0",
- "@tsparticles/shape-circle": "4.0.0-beta.0",
- "@tsparticles/updater-fill-color": "4.0.0-beta.0",
- "@tsparticles/updater-opacity": "4.0.0-beta.0",
- "@tsparticles/updater-out-modes": "4.0.0-beta.0",
- "@tsparticles/updater-size": "4.0.0-beta.0"
+ "@tsparticles/engine": "4.0.0-beta.1",
+ "@tsparticles/plugin-hex-color": "4.0.0-beta.1",
+ "@tsparticles/plugin-hsl-color": "4.0.0-beta.1",
+ "@tsparticles/plugin-move": "4.0.0-beta.1",
+ "@tsparticles/plugin-rgb-color": "4.0.0-beta.1",
+ "@tsparticles/shape-circle": "4.0.0-beta.1",
+ "@tsparticles/updater-fill-color": "4.0.0-beta.1",
+ "@tsparticles/updater-opacity": "4.0.0-beta.1",
+ "@tsparticles/updater-out-modes": "4.0.0-beta.1",
+ "@tsparticles/updater-size": "4.0.0-beta.1"
},
"publishConfig": {
"access": "public"
diff --git a/bundles/basic/package.json b/bundles/basic/package.json
index 477f7304dea..b37afc0adbd 100644
--- a/bundles/basic/package.json
+++ b/bundles/basic/package.json
@@ -1,6 +1,6 @@
{
"name": "@tsparticles/basic",
- "version": "4.0.0-beta.0",
+ "version": "4.0.0-beta.1",
"description": "Easily create highly customizable particle animations and use them as animated backgrounds for your website. Ready to use components available also for React, Vue.js (2.x and 3.x), Angular, Svelte, jQuery, Preact, Riot.js, Inferno.",
"homepage": "https://particles.js.org",
"scripts": {
@@ -107,16 +107,16 @@
"./package.json": "./dist/package.json"
},
"dependencies": {
- "@tsparticles/engine": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-hex-color": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-hsl-color": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-move": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-rgb-color": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-circle": "workspace:4.0.0-beta.0",
- "@tsparticles/updater-fill-color": "workspace:4.0.0-beta.0",
- "@tsparticles/updater-opacity": "workspace:4.0.0-beta.0",
- "@tsparticles/updater-out-modes": "workspace:4.0.0-beta.0",
- "@tsparticles/updater-size": "workspace:4.0.0-beta.0"
+ "@tsparticles/engine": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-hex-color": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-hsl-color": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-move": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-rgb-color": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-circle": "workspace:4.0.0-beta.1",
+ "@tsparticles/updater-fill-color": "workspace:4.0.0-beta.1",
+ "@tsparticles/updater-opacity": "workspace:4.0.0-beta.1",
+ "@tsparticles/updater-out-modes": "workspace:4.0.0-beta.1",
+ "@tsparticles/updater-size": "workspace:4.0.0-beta.1"
},
"publishConfig": {
"access": "public",
diff --git a/bundles/basic/src/index.ts b/bundles/basic/src/index.ts
index 61782736dfe..5e6b5498774 100644
--- a/bundles/basic/src/index.ts
+++ b/bundles/basic/src/index.ts
@@ -13,7 +13,7 @@ declare const __VERSION__: string;
export async function loadBasic(engine: Engine): Promise {
engine.checkVersion(__VERSION__);
- await engine.register(async e => {
+ await engine.pluginManager.register(async e => {
const [
{ loadHexColorPlugin },
{ loadHslColorPlugin },
diff --git a/bundles/confetti/CHANGELOG.md b/bundles/confetti/CHANGELOG.md
index d13804347ac..c45d78b9904 100644
--- a/bundles/confetti/CHANGELOG.md
+++ b/bundles/confetti/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [4.0.0-beta.1](https://github.com/tsparticles/tsparticles/compare/v4.0.0-beta.0...v4.0.0-beta.1) (2026-03-31)
+
+**Note:** Version bump only for package @tsparticles/confetti
+
# [4.0.0-beta.0](https://github.com/tsparticles/tsparticles/compare/v4.0.0-alpha.28...v4.0.0-beta.0) (2026-03-19)
**Note:** Version bump only for package @tsparticles/confetti
diff --git a/bundles/confetti/package.dist.json b/bundles/confetti/package.dist.json
index e637f3bd8de..0acc10a54d5 100644
--- a/bundles/confetti/package.dist.json
+++ b/bundles/confetti/package.dist.json
@@ -1,6 +1,6 @@
{
"name": "@tsparticles/confetti",
- "version": "4.0.0-beta.0",
+ "version": "4.0.0-beta.1",
"description": "Easily create highly customizable particle animations and use them as animated backgrounds for your website. Ready to use components available also for React, Vue.js (2.x and 3.x), Angular, Svelte, jQuery, Preact, Riot.js, Inferno.",
"homepage": "https://particles.js.org",
"repository": {
@@ -99,22 +99,22 @@
"./package.json": "./package.json"
},
"dependencies": {
- "@tsparticles/basic": "4.0.0-beta.0",
- "@tsparticles/engine": "4.0.0-beta.0",
- "@tsparticles/plugin-emitters": "4.0.0-beta.0",
- "@tsparticles/plugin-motion": "4.0.0-beta.0",
- "@tsparticles/shape-cards": "4.0.0-beta.0",
- "@tsparticles/shape-emoji": "4.0.0-beta.0",
- "@tsparticles/shape-heart": "4.0.0-beta.0",
- "@tsparticles/shape-image": "4.0.0-beta.0",
- "@tsparticles/shape-polygon": "4.0.0-beta.0",
- "@tsparticles/shape-square": "4.0.0-beta.0",
- "@tsparticles/shape-star": "4.0.0-beta.0",
- "@tsparticles/updater-life": "4.0.0-beta.0",
- "@tsparticles/updater-roll": "4.0.0-beta.0",
- "@tsparticles/updater-rotate": "4.0.0-beta.0",
- "@tsparticles/updater-tilt": "4.0.0-beta.0",
- "@tsparticles/updater-wobble": "4.0.0-beta.0"
+ "@tsparticles/basic": "4.0.0-beta.1",
+ "@tsparticles/engine": "4.0.0-beta.1",
+ "@tsparticles/plugin-emitters": "4.0.0-beta.1",
+ "@tsparticles/plugin-motion": "4.0.0-beta.1",
+ "@tsparticles/shape-cards": "4.0.0-beta.1",
+ "@tsparticles/shape-emoji": "4.0.0-beta.1",
+ "@tsparticles/shape-heart": "4.0.0-beta.1",
+ "@tsparticles/shape-image": "4.0.0-beta.1",
+ "@tsparticles/shape-polygon": "4.0.0-beta.1",
+ "@tsparticles/shape-square": "4.0.0-beta.1",
+ "@tsparticles/shape-star": "4.0.0-beta.1",
+ "@tsparticles/updater-life": "4.0.0-beta.1",
+ "@tsparticles/updater-roll": "4.0.0-beta.1",
+ "@tsparticles/updater-rotate": "4.0.0-beta.1",
+ "@tsparticles/updater-tilt": "4.0.0-beta.1",
+ "@tsparticles/updater-wobble": "4.0.0-beta.1"
},
"publishConfig": {
"access": "public"
diff --git a/bundles/confetti/package.json b/bundles/confetti/package.json
index aa86cce94e1..c773ab0df99 100644
--- a/bundles/confetti/package.json
+++ b/bundles/confetti/package.json
@@ -1,6 +1,6 @@
{
"name": "@tsparticles/confetti",
- "version": "4.0.0-beta.0",
+ "version": "4.0.0-beta.1",
"description": "Easily create highly customizable particle animations and use them as animated backgrounds for your website. Ready to use components available also for React, Vue.js (2.x and 3.x), Angular, Svelte, jQuery, Preact, Riot.js, Inferno.",
"homepage": "https://particles.js.org",
"scripts": {
@@ -107,22 +107,22 @@
"./package.json": "./dist/package.json"
},
"dependencies": {
- "@tsparticles/basic": "workspace:4.0.0-beta.0",
- "@tsparticles/engine": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-emitters": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-motion": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-cards": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-emoji": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-heart": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-image": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-polygon": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-square": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-star": "workspace:4.0.0-beta.0",
- "@tsparticles/updater-life": "workspace:4.0.0-beta.0",
- "@tsparticles/updater-roll": "workspace:4.0.0-beta.0",
- "@tsparticles/updater-rotate": "workspace:4.0.0-beta.0",
- "@tsparticles/updater-tilt": "workspace:4.0.0-beta.0",
- "@tsparticles/updater-wobble": "workspace:4.0.0-beta.0"
+ "@tsparticles/basic": "workspace:4.0.0-beta.1",
+ "@tsparticles/engine": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-emitters": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-motion": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-cards": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-emoji": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-heart": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-image": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-polygon": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-square": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-star": "workspace:4.0.0-beta.1",
+ "@tsparticles/updater-life": "workspace:4.0.0-beta.1",
+ "@tsparticles/updater-roll": "workspace:4.0.0-beta.1",
+ "@tsparticles/updater-rotate": "workspace:4.0.0-beta.1",
+ "@tsparticles/updater-tilt": "workspace:4.0.0-beta.1",
+ "@tsparticles/updater-wobble": "workspace:4.0.0-beta.1"
},
"publishConfig": {
"access": "public",
diff --git a/bundles/confetti/src/confetti.ts b/bundles/confetti/src/confetti.ts
index bfce3b6f639..bcb1b302443 100644
--- a/bundles/confetti/src/confetti.ts
+++ b/bundles/confetti/src/confetti.ts
@@ -99,7 +99,7 @@ async function initPlugins(engine: Engine): Promise {
engine.checkVersion(__VERSION__);
- await engine.register(async e => {
+ await engine.pluginManager.register(async e => {
const [
{ loadBasic },
{ loadEmittersPlugin },
diff --git a/bundles/fireworks/CHANGELOG.md b/bundles/fireworks/CHANGELOG.md
index 8d9309ce063..e2854be5408 100644
--- a/bundles/fireworks/CHANGELOG.md
+++ b/bundles/fireworks/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [4.0.0-beta.1](https://github.com/tsparticles/tsparticles/compare/v4.0.0-beta.0...v4.0.0-beta.1) (2026-03-31)
+
+**Note:** Version bump only for package @tsparticles/fireworks
+
# [4.0.0-beta.0](https://github.com/tsparticles/tsparticles/compare/v4.0.0-alpha.28...v4.0.0-beta.0) (2026-03-19)
**Note:** Version bump only for package @tsparticles/fireworks
diff --git a/bundles/fireworks/package.dist.json b/bundles/fireworks/package.dist.json
index d67907fe88c..1302032063d 100644
--- a/bundles/fireworks/package.dist.json
+++ b/bundles/fireworks/package.dist.json
@@ -1,6 +1,6 @@
{
"name": "@tsparticles/fireworks",
- "version": "4.0.0-beta.0",
+ "version": "4.0.0-beta.1",
"description": "Easily create highly customizable particle animations and use them as animated backgrounds for your website. Ready to use components available also for React, Vue.js (2.x and 3.x), Angular, Svelte, jQuery, Preact, Riot.js, Inferno.",
"homepage": "https://particles.js.org",
"repository": {
@@ -99,15 +99,15 @@
"./package.json": "./package.json"
},
"dependencies": {
- "@tsparticles/basic": "4.0.0-beta.0",
- "@tsparticles/effect-trail": "4.0.0-beta.0",
- "@tsparticles/engine": "4.0.0-beta.0",
- "@tsparticles/plugin-emitters": "4.0.0-beta.0",
- "@tsparticles/plugin-emitters-shape-square": "4.0.0-beta.0",
- "@tsparticles/plugin-sounds": "4.0.0-beta.0",
- "@tsparticles/updater-destroy": "4.0.0-beta.0",
- "@tsparticles/updater-life": "4.0.0-beta.0",
- "@tsparticles/updater-rotate": "4.0.0-beta.0"
+ "@tsparticles/basic": "4.0.0-beta.1",
+ "@tsparticles/effect-trail": "4.0.0-beta.1",
+ "@tsparticles/engine": "4.0.0-beta.1",
+ "@tsparticles/plugin-emitters": "4.0.0-beta.1",
+ "@tsparticles/plugin-emitters-shape-square": "4.0.0-beta.1",
+ "@tsparticles/plugin-sounds": "4.0.0-beta.1",
+ "@tsparticles/updater-destroy": "4.0.0-beta.1",
+ "@tsparticles/updater-life": "4.0.0-beta.1",
+ "@tsparticles/updater-rotate": "4.0.0-beta.1"
},
"publishConfig": {
"access": "public"
diff --git a/bundles/fireworks/package.json b/bundles/fireworks/package.json
index 95d701428e5..a1df9c1f8a3 100644
--- a/bundles/fireworks/package.json
+++ b/bundles/fireworks/package.json
@@ -1,6 +1,6 @@
{
"name": "@tsparticles/fireworks",
- "version": "4.0.0-beta.0",
+ "version": "4.0.0-beta.1",
"description": "Easily create highly customizable particle animations and use them as animated backgrounds for your website. Ready to use components available also for React, Vue.js (2.x and 3.x), Angular, Svelte, jQuery, Preact, Riot.js, Inferno.",
"homepage": "https://particles.js.org",
"scripts": {
@@ -107,15 +107,15 @@
"./package.json": "./dist/package.json"
},
"dependencies": {
- "@tsparticles/basic": "workspace:4.0.0-beta.0",
- "@tsparticles/effect-trail": "workspace:4.0.0-beta.0",
- "@tsparticles/engine": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-emitters": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-emitters-shape-square": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-sounds": "workspace:4.0.0-beta.0",
- "@tsparticles/updater-destroy": "workspace:4.0.0-beta.0",
- "@tsparticles/updater-life": "workspace:4.0.0-beta.0",
- "@tsparticles/updater-rotate": "workspace:4.0.0-beta.0"
+ "@tsparticles/basic": "workspace:4.0.0-beta.1",
+ "@tsparticles/effect-trail": "workspace:4.0.0-beta.1",
+ "@tsparticles/engine": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-emitters": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-emitters-shape-square": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-sounds": "workspace:4.0.0-beta.1",
+ "@tsparticles/updater-destroy": "workspace:4.0.0-beta.1",
+ "@tsparticles/updater-life": "workspace:4.0.0-beta.1",
+ "@tsparticles/updater-rotate": "workspace:4.0.0-beta.1"
},
"publishConfig": {
"access": "public",
diff --git a/bundles/fireworks/src/fireworks.ts b/bundles/fireworks/src/fireworks.ts
index 448d6b7ed98..7c650ff244d 100644
--- a/bundles/fireworks/src/fireworks.ts
+++ b/bundles/fireworks/src/fireworks.ts
@@ -77,7 +77,7 @@ async function initPlugins(engine: Engine): Promise {
engine.checkVersion(__VERSION__);
- await engine.register(async e => {
+ await engine.pluginManager.register(async e => {
const [
{ loadBasic },
{ loadEmittersPlugin },
diff --git a/bundles/full/CHANGELOG.md b/bundles/full/CHANGELOG.md
index 5b0f343124c..affa6715be0 100644
--- a/bundles/full/CHANGELOG.md
+++ b/bundles/full/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [4.0.0-beta.1](https://github.com/tsparticles/tsparticles/compare/v4.0.0-beta.0...v4.0.0-beta.1) (2026-03-31)
+
+**Note:** Version bump only for package tsparticles
+
# [4.0.0-beta.0](https://github.com/tsparticles/tsparticles/compare/v4.0.0-alpha.28...v4.0.0-beta.0) (2026-03-19)
### Features
diff --git a/bundles/full/package.dist.json b/bundles/full/package.dist.json
index 81109e4081a..7dc7c7b7f62 100644
--- a/bundles/full/package.dist.json
+++ b/bundles/full/package.dist.json
@@ -1,6 +1,6 @@
{
"name": "tsparticles",
- "version": "4.0.0-beta.0",
+ "version": "4.0.0-beta.1",
"description": "Easily create highly customizable particle animations and use them as animated backgrounds for your website. Ready to use components available also for React, Vue.js (2.x and 3.x), Angular, Svelte, jQuery, Preact, Riot.js, Inferno.",
"homepage": "https://particles.js.org",
"repository": {
@@ -99,20 +99,20 @@
"./package.json": "./package.json"
},
"dependencies": {
- "@tsparticles/engine": "4.0.0-beta.0",
- "@tsparticles/interaction-external-drag": "4.0.0-beta.0",
- "@tsparticles/interaction-external-trail": "4.0.0-beta.0",
- "@tsparticles/plugin-absorbers": "4.0.0-beta.0",
- "@tsparticles/plugin-emitters": "4.0.0-beta.0",
- "@tsparticles/plugin-emitters-shape-circle": "4.0.0-beta.0",
- "@tsparticles/plugin-emitters-shape-square": "4.0.0-beta.0",
- "@tsparticles/shape-text": "4.0.0-beta.0",
- "@tsparticles/slim": "4.0.0-beta.0",
- "@tsparticles/updater-destroy": "4.0.0-beta.0",
- "@tsparticles/updater-roll": "4.0.0-beta.0",
- "@tsparticles/updater-tilt": "4.0.0-beta.0",
- "@tsparticles/updater-twinkle": "4.0.0-beta.0",
- "@tsparticles/updater-wobble": "4.0.0-beta.0"
+ "@tsparticles/engine": "4.0.0-beta.1",
+ "@tsparticles/interaction-external-drag": "4.0.0-beta.1",
+ "@tsparticles/interaction-external-trail": "4.0.0-beta.1",
+ "@tsparticles/plugin-absorbers": "4.0.0-beta.1",
+ "@tsparticles/plugin-emitters": "4.0.0-beta.1",
+ "@tsparticles/plugin-emitters-shape-circle": "4.0.0-beta.1",
+ "@tsparticles/plugin-emitters-shape-square": "4.0.0-beta.1",
+ "@tsparticles/shape-text": "4.0.0-beta.1",
+ "@tsparticles/slim": "4.0.0-beta.1",
+ "@tsparticles/updater-destroy": "4.0.0-beta.1",
+ "@tsparticles/updater-roll": "4.0.0-beta.1",
+ "@tsparticles/updater-tilt": "4.0.0-beta.1",
+ "@tsparticles/updater-twinkle": "4.0.0-beta.1",
+ "@tsparticles/updater-wobble": "4.0.0-beta.1"
},
"publishConfig": {
"access": "public"
diff --git a/bundles/full/package.json b/bundles/full/package.json
index d0f14d8e1b7..7e21662d523 100644
--- a/bundles/full/package.json
+++ b/bundles/full/package.json
@@ -1,6 +1,6 @@
{
"name": "tsparticles",
- "version": "4.0.0-beta.0",
+ "version": "4.0.0-beta.1",
"description": "Easily create highly customizable particle animations and use them as animated backgrounds for your website. Ready to use components available also for React, Vue.js (2.x and 3.x), Angular, Svelte, jQuery, Preact, Riot.js, Inferno.",
"homepage": "https://particles.js.org",
"scripts": {
@@ -107,20 +107,20 @@
"./package.json": "./dist/package.json"
},
"dependencies": {
- "@tsparticles/engine": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-external-drag": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-external-trail": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-absorbers": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-emitters": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-emitters-shape-circle": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-emitters-shape-square": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-text": "workspace:4.0.0-beta.0",
- "@tsparticles/slim": "workspace:4.0.0-beta.0",
- "@tsparticles/updater-destroy": "workspace:4.0.0-beta.0",
- "@tsparticles/updater-roll": "workspace:4.0.0-beta.0",
- "@tsparticles/updater-tilt": "workspace:4.0.0-beta.0",
- "@tsparticles/updater-twinkle": "workspace:4.0.0-beta.0",
- "@tsparticles/updater-wobble": "workspace:4.0.0-beta.0"
+ "@tsparticles/engine": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-external-drag": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-external-trail": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-absorbers": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-emitters": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-emitters-shape-circle": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-emitters-shape-square": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-text": "workspace:4.0.0-beta.1",
+ "@tsparticles/slim": "workspace:4.0.0-beta.1",
+ "@tsparticles/updater-destroy": "workspace:4.0.0-beta.1",
+ "@tsparticles/updater-roll": "workspace:4.0.0-beta.1",
+ "@tsparticles/updater-tilt": "workspace:4.0.0-beta.1",
+ "@tsparticles/updater-twinkle": "workspace:4.0.0-beta.1",
+ "@tsparticles/updater-wobble": "workspace:4.0.0-beta.1"
},
"publishConfig": {
"access": "public",
diff --git a/bundles/full/src/index.ts b/bundles/full/src/index.ts
index 4c040a6bd42..3508a3b0bb1 100644
--- a/bundles/full/src/index.ts
+++ b/bundles/full/src/index.ts
@@ -13,7 +13,7 @@ declare const __VERSION__: string;
export async function loadFull(engine: Engine): Promise {
engine.checkVersion(__VERSION__);
- await engine.register(async e => {
+ await engine.pluginManager.register(async e => {
const [
{ loadSlim },
{ loadExternalDragInteraction },
diff --git a/bundles/pjs/CHANGELOG.md b/bundles/pjs/CHANGELOG.md
index 03efd66aa09..32996eb3e1c 100644
--- a/bundles/pjs/CHANGELOG.md
+++ b/bundles/pjs/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [4.0.0-beta.1](https://github.com/tsparticles/tsparticles/compare/v4.0.0-beta.0...v4.0.0-beta.1) (2026-03-31)
+
+**Note:** Version bump only for package @tsparticles/pjs
+
# [4.0.0-beta.0](https://github.com/tsparticles/tsparticles/compare/v4.0.0-alpha.28...v4.0.0-beta.0) (2026-03-19)
**Note:** Version bump only for package @tsparticles/pjs
diff --git a/bundles/pjs/package.dist.json b/bundles/pjs/package.dist.json
index c55597b8987..289ad5d87f8 100644
--- a/bundles/pjs/package.dist.json
+++ b/bundles/pjs/package.dist.json
@@ -1,6 +1,6 @@
{
"name": "@tsparticles/pjs",
- "version": "4.0.0-beta.0",
+ "version": "4.0.0-beta.1",
"description": "Easily create highly customizable particle animations and use them as animated backgrounds for your website. Ready to use components available also for React, Vue.js (2.x and 3.x), Angular, Svelte, jQuery, Preact, Riot.js, Inferno.",
"homepage": "https://particles.js.org",
"repository": {
@@ -99,9 +99,9 @@
"./package.json": "./package.json"
},
"dependencies": {
- "@tsparticles/engine": "4.0.0-beta.0",
- "@tsparticles/plugin-responsive": "4.0.0-beta.0",
- "tsparticles": "4.0.0-beta.0"
+ "@tsparticles/engine": "4.0.0-beta.1",
+ "@tsparticles/plugin-responsive": "4.0.0-beta.1",
+ "tsparticles": "4.0.0-beta.1"
},
"publishConfig": {
"access": "public"
diff --git a/bundles/pjs/package.json b/bundles/pjs/package.json
index 3e9edbe990f..c1485db308c 100644
--- a/bundles/pjs/package.json
+++ b/bundles/pjs/package.json
@@ -1,6 +1,6 @@
{
"name": "@tsparticles/pjs",
- "version": "4.0.0-beta.0",
+ "version": "4.0.0-beta.1",
"description": "Easily create highly customizable particle animations and use them as animated backgrounds for your website. Ready to use components available also for React, Vue.js (2.x and 3.x), Angular, Svelte, jQuery, Preact, Riot.js, Inferno.",
"homepage": "https://particles.js.org",
"scripts": {
@@ -107,12 +107,12 @@
"./package.json": "./dist/package.json"
},
"dependencies": {
- "@tsparticles/engine": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-responsive": "workspace:4.0.0-beta.0",
- "tsparticles": "workspace:4.0.0-beta.0"
+ "@tsparticles/engine": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-responsive": "workspace:4.0.0-beta.1",
+ "tsparticles": "workspace:4.0.0-beta.1"
},
"devDependencies": {
- "@tsparticles/plugin-interactivity": "workspace:4.0.0-beta.0"
+ "@tsparticles/plugin-interactivity": "workspace:4.0.0-beta.1"
},
"publishConfig": {
"access": "public",
diff --git a/bundles/pjs/src/VincentGarreau/particles.ts b/bundles/pjs/src/VincentGarreau/particles.ts
index fb773b8a376..a95d4a1ef1f 100644
--- a/bundles/pjs/src/VincentGarreau/particles.ts
+++ b/bundles/pjs/src/VincentGarreau/particles.ts
@@ -309,7 +309,7 @@ const defaultMinOpacity = 0,
*/
// eslint-disable-next-line @typescript-eslint/no-deprecated
particlesJS.setOnClickHandler = (callback: (e: Event, particles?: Particle[]) => void): void => {
- engine.setOnClickHandler?.(callback);
+ engine.pluginManager.setOnClickHandler?.(callback);
};
/**
diff --git a/bundles/pjs/src/index.ts b/bundles/pjs/src/index.ts
index a789f381d0f..238168b4010 100644
--- a/bundles/pjs/src/index.ts
+++ b/bundles/pjs/src/index.ts
@@ -34,7 +34,7 @@ declare global {
const initPjs = async (engine: Engine): Promise => {
engine.checkVersion(__VERSION__);
- await engine.register(async e => {
+ await engine.pluginManager.register(async e => {
const [
{ loadFull },
{ loadResponsivePlugin },
diff --git a/bundles/slim/CHANGELOG.md b/bundles/slim/CHANGELOG.md
index 2d774771762..12095b52bad 100644
--- a/bundles/slim/CHANGELOG.md
+++ b/bundles/slim/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [4.0.0-beta.1](https://github.com/tsparticles/tsparticles/compare/v4.0.0-beta.0...v4.0.0-beta.1) (2026-03-31)
+
+**Note:** Version bump only for package @tsparticles/slim
+
# [4.0.0-beta.0](https://github.com/tsparticles/tsparticles/compare/v4.0.0-alpha.28...v4.0.0-beta.0) (2026-03-19)
**Note:** Version bump only for package @tsparticles/slim
diff --git a/bundles/slim/package.dist.json b/bundles/slim/package.dist.json
index e79350186f7..99f4ba49c07 100644
--- a/bundles/slim/package.dist.json
+++ b/bundles/slim/package.dist.json
@@ -1,6 +1,6 @@
{
"name": "@tsparticles/slim",
- "version": "4.0.0-beta.0",
+ "version": "4.0.0-beta.1",
"description": "Easily create highly customizable particle animations and use them as animated backgrounds for your website. Ready to use components available also for React, Vue.js (2.x and 3.x), Angular, Svelte, jQuery, Preact, Riot.js, Inferno.",
"homepage": "https://particles.js.org",
"repository": {
@@ -99,33 +99,33 @@
"./package.json": "./package.json"
},
"dependencies": {
- "@tsparticles/basic": "4.0.0-beta.0",
- "@tsparticles/engine": "4.0.0-beta.0",
- "@tsparticles/interaction-external-attract": "4.0.0-beta.0",
- "@tsparticles/interaction-external-bounce": "4.0.0-beta.0",
- "@tsparticles/interaction-external-bubble": "4.0.0-beta.0",
- "@tsparticles/interaction-external-connect": "4.0.0-beta.0",
- "@tsparticles/interaction-external-grab": "4.0.0-beta.0",
- "@tsparticles/interaction-external-parallax": "4.0.0-beta.0",
- "@tsparticles/interaction-external-pause": "4.0.0-beta.0",
- "@tsparticles/interaction-external-push": "4.0.0-beta.0",
- "@tsparticles/interaction-external-remove": "4.0.0-beta.0",
- "@tsparticles/interaction-external-repulse": "4.0.0-beta.0",
- "@tsparticles/interaction-external-slow": "4.0.0-beta.0",
- "@tsparticles/interaction-particles-attract": "4.0.0-beta.0",
- "@tsparticles/interaction-particles-collisions": "4.0.0-beta.0",
- "@tsparticles/interaction-particles-links": "4.0.0-beta.0",
- "@tsparticles/plugin-easing-quad": "4.0.0-beta.0",
- "@tsparticles/plugin-interactivity": "4.0.0-beta.0",
- "@tsparticles/shape-emoji": "4.0.0-beta.0",
- "@tsparticles/shape-image": "4.0.0-beta.0",
- "@tsparticles/shape-line": "4.0.0-beta.0",
- "@tsparticles/shape-polygon": "4.0.0-beta.0",
- "@tsparticles/shape-square": "4.0.0-beta.0",
- "@tsparticles/shape-star": "4.0.0-beta.0",
- "@tsparticles/updater-life": "4.0.0-beta.0",
- "@tsparticles/updater-rotate": "4.0.0-beta.0",
- "@tsparticles/updater-stroke-color": "4.0.0-beta.0"
+ "@tsparticles/basic": "4.0.0-beta.1",
+ "@tsparticles/engine": "4.0.0-beta.1",
+ "@tsparticles/interaction-external-attract": "4.0.0-beta.1",
+ "@tsparticles/interaction-external-bounce": "4.0.0-beta.1",
+ "@tsparticles/interaction-external-bubble": "4.0.0-beta.1",
+ "@tsparticles/interaction-external-connect": "4.0.0-beta.1",
+ "@tsparticles/interaction-external-grab": "4.0.0-beta.1",
+ "@tsparticles/interaction-external-parallax": "4.0.0-beta.1",
+ "@tsparticles/interaction-external-pause": "4.0.0-beta.1",
+ "@tsparticles/interaction-external-push": "4.0.0-beta.1",
+ "@tsparticles/interaction-external-remove": "4.0.0-beta.1",
+ "@tsparticles/interaction-external-repulse": "4.0.0-beta.1",
+ "@tsparticles/interaction-external-slow": "4.0.0-beta.1",
+ "@tsparticles/interaction-particles-attract": "4.0.0-beta.1",
+ "@tsparticles/interaction-particles-collisions": "4.0.0-beta.1",
+ "@tsparticles/interaction-particles-links": "4.0.0-beta.1",
+ "@tsparticles/plugin-easing-quad": "4.0.0-beta.1",
+ "@tsparticles/plugin-interactivity": "4.0.0-beta.1",
+ "@tsparticles/shape-emoji": "4.0.0-beta.1",
+ "@tsparticles/shape-image": "4.0.0-beta.1",
+ "@tsparticles/shape-line": "4.0.0-beta.1",
+ "@tsparticles/shape-polygon": "4.0.0-beta.1",
+ "@tsparticles/shape-square": "4.0.0-beta.1",
+ "@tsparticles/shape-star": "4.0.0-beta.1",
+ "@tsparticles/updater-life": "4.0.0-beta.1",
+ "@tsparticles/updater-rotate": "4.0.0-beta.1",
+ "@tsparticles/updater-stroke-color": "4.0.0-beta.1"
},
"publishConfig": {
"access": "public"
diff --git a/bundles/slim/package.json b/bundles/slim/package.json
index f3c6ecd9eb3..f0ef622f704 100644
--- a/bundles/slim/package.json
+++ b/bundles/slim/package.json
@@ -1,6 +1,6 @@
{
"name": "@tsparticles/slim",
- "version": "4.0.0-beta.0",
+ "version": "4.0.0-beta.1",
"description": "Easily create highly customizable particle animations and use them as animated backgrounds for your website. Ready to use components available also for React, Vue.js (2.x and 3.x), Angular, Svelte, jQuery, Preact, Riot.js, Inferno.",
"homepage": "https://particles.js.org",
"scripts": {
@@ -107,33 +107,33 @@
"./package.json": "./dist/package.json"
},
"dependencies": {
- "@tsparticles/basic": "workspace:4.0.0-beta.0",
- "@tsparticles/engine": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-external-attract": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-external-bounce": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-external-bubble": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-external-connect": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-external-grab": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-external-parallax": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-external-pause": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-external-push": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-external-remove": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-external-repulse": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-external-slow": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-particles-attract": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-particles-collisions": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-particles-links": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-easing-quad": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-interactivity": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-emoji": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-image": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-line": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-polygon": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-square": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-star": "workspace:4.0.0-beta.0",
- "@tsparticles/updater-life": "workspace:4.0.0-beta.0",
- "@tsparticles/updater-rotate": "workspace:4.0.0-beta.0",
- "@tsparticles/updater-stroke-color": "workspace:4.0.0-beta.0"
+ "@tsparticles/basic": "workspace:4.0.0-beta.1",
+ "@tsparticles/engine": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-external-attract": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-external-bounce": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-external-bubble": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-external-connect": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-external-grab": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-external-parallax": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-external-pause": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-external-push": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-external-remove": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-external-repulse": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-external-slow": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-particles-attract": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-particles-collisions": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-particles-links": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-easing-quad": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-interactivity": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-emoji": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-image": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-line": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-polygon": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-square": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-star": "workspace:4.0.0-beta.1",
+ "@tsparticles/updater-life": "workspace:4.0.0-beta.1",
+ "@tsparticles/updater-rotate": "workspace:4.0.0-beta.1",
+ "@tsparticles/updater-stroke-color": "workspace:4.0.0-beta.1"
},
"publishConfig": {
"access": "public",
diff --git a/bundles/slim/src/index.ts b/bundles/slim/src/index.ts
index c2ed097482a..d75979eacd9 100644
--- a/bundles/slim/src/index.ts
+++ b/bundles/slim/src/index.ts
@@ -13,7 +13,7 @@ declare const __VERSION__: string;
export async function loadSlim(engine: Engine): Promise {
engine.checkVersion(__VERSION__);
- await engine.register(async e => {
+ await engine.pluginManager.register(async e => {
const [
{ loadBasic },
diff --git a/demo/electron/CHANGELOG.md b/demo/electron/CHANGELOG.md
index 6e3a29ade46..78c7055d8a9 100644
--- a/demo/electron/CHANGELOG.md
+++ b/demo/electron/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [4.0.0-beta.1](https://github.com/tsparticles/tsparticles/compare/v4.0.0-beta.0...v4.0.0-beta.1) (2026-03-31)
+
+**Note:** Version bump only for package @tsparticles/electron-demo
+
# [4.0.0-beta.0](https://github.com/tsparticles/tsparticles/compare/v4.0.0-alpha.28...v4.0.0-beta.0) (2026-03-19)
**Note:** Version bump only for package @tsparticles/electron-demo
diff --git a/demo/electron/package.json b/demo/electron/package.json
index 937a9e9b17c..52acef68b45 100644
--- a/demo/electron/package.json
+++ b/demo/electron/package.json
@@ -1,6 +1,6 @@
{
"name": "@tsparticles/electron-demo",
- "version": "4.0.0-beta.0",
+ "version": "4.0.0-beta.1",
"description": "",
"main": "app/index.js",
"private": true,
@@ -14,9 +14,9 @@
"author": "Matteo Bruni ",
"license": "MIT",
"dependencies": {
- "@tsparticles/configs": "workspace:4.0.0-beta.0",
- "@tsparticles/engine": "workspace:4.0.0-beta.0",
- "tsparticles": "workspace:4.0.0-beta.0"
+ "@tsparticles/configs": "workspace:4.0.0-beta.1",
+ "@tsparticles/engine": "workspace:4.0.0-beta.1",
+ "tsparticles": "workspace:4.0.0-beta.1"
},
"devDependencies": {
"electron": "^41.0.3"
diff --git a/demo/vanilla/CHANGELOG.md b/demo/vanilla/CHANGELOG.md
index 2a8de17013d..d1256fd017c 100644
--- a/demo/vanilla/CHANGELOG.md
+++ b/demo/vanilla/CHANGELOG.md
@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [4.0.0-beta.1](https://github.com/tsparticles/tsparticles/compare/v4.0.0-beta.0...v4.0.0-beta.1) (2026-03-31)
+
+### Features
+
+- added cannon external interaction ([e133ab3](https://github.com/tsparticles/tsparticles/commit/e133ab35835b6411c0da1b151b046a36bcfd9ee1))
+
# [4.0.0-beta.0](https://github.com/tsparticles/tsparticles/compare/v4.0.0-alpha.28...v4.0.0-beta.0) (2026-03-19)
### Features
diff --git a/demo/vanilla/app.ts b/demo/vanilla/app.ts
index 1e5fb37a179..e421a69ef22 100644
--- a/demo/vanilla/app.ts
+++ b/demo/vanilla/app.ts
@@ -117,6 +117,7 @@ app.use("/plugin-emitters", express.static("./node_modules/@tsparticles/plugin-e
app.use("/plugin-interactivity", express.static("./node_modules/@tsparticles/plugin-interactivity"));
app.use("/plugin-polygon-mask", express.static("./node_modules/@tsparticles/plugin-polygon-mask"));
app.use("/plugin-poisson-disc", express.static("./node_modules/@tsparticles/plugin-poisson-disc"));
+app.use("/interaction-external-cannon", express.static("./node_modules/@tsparticles/interaction-external-cannon"));
app.use("/interaction-external-parallax", express.static("./node_modules/@tsparticles/interaction-external-parallax"));
app.use("/interaction-external-particle", express.static("./node_modules/@tsparticles/interaction-external-particle"));
app.use("/interaction-external-pop", express.static("./node_modules/@tsparticles/interaction-external-pop"));
diff --git a/demo/vanilla/package.json b/demo/vanilla/package.json
index 2ade1747756..89b10a2dacf 100644
--- a/demo/vanilla/package.json
+++ b/demo/vanilla/package.json
@@ -1,7 +1,7 @@
{
"name": "@tsparticles/demo",
"private": true,
- "version": "4.0.0-beta.0",
+ "version": "4.0.0-beta.1",
"description": "> TODO: description",
"author": "Matteo Bruni ",
"homepage": "https://particles.js.org",
@@ -50,142 +50,143 @@
"winston": "^3.19.0"
},
"dependencies": {
- "@tsparticles/all": "workspace:4.0.0-beta.0",
- "@tsparticles/basic": "workspace:4.0.0-beta.0",
- "@tsparticles/canvas-utils": "workspace:4.0.0-beta.0",
- "@tsparticles/confetti": "workspace:4.0.0-beta.0",
- "@tsparticles/configs": "workspace:4.0.0-beta.0",
- "@tsparticles/effect-bubble": "workspace:4.0.0-beta.0",
- "@tsparticles/effect-particles": "workspace:4.0.0-beta.0",
- "@tsparticles/effect-shadow": "workspace:4.0.0-beta.0",
- "@tsparticles/effect-trail": "workspace:4.0.0-beta.0",
- "@tsparticles/engine": "workspace:4.0.0-beta.0",
- "@tsparticles/fireworks": "workspace:4.0.0-beta.0",
- "@tsparticles/fractal-noise": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-external-attract": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-external-bounce": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-external-bubble": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-external-connect": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-external-drag": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-external-grab": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-external-parallax": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-external-particle": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-external-pause": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-external-pop": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-external-push": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-external-remove": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-external-repulse": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-external-slow": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-external-trail": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-light": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-particles-attract": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-particles-collisions": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-particles-links": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-particles-repulse": "workspace:4.0.0-beta.0",
- "@tsparticles/noise-field": "workspace:4.0.0-beta.0",
- "@tsparticles/path-branches": "workspace:4.0.0-beta.0",
- "@tsparticles/path-brownian": "workspace:4.0.0-beta.0",
- "@tsparticles/path-curl-noise": "workspace:4.0.0-beta.0",
- "@tsparticles/path-curves": "workspace:4.0.0-beta.0",
- "@tsparticles/path-fractal-noise": "workspace:4.0.0-beta.0",
- "@tsparticles/path-grid": "workspace:4.0.0-beta.0",
- "@tsparticles/path-levy": "workspace:4.0.0-beta.0",
- "@tsparticles/path-perlin-noise": "workspace:4.0.0-beta.0",
- "@tsparticles/path-polygon": "workspace:4.0.0-beta.0",
- "@tsparticles/path-random": "workspace:4.0.0-beta.0",
- "@tsparticles/path-simplex-noise": "workspace:4.0.0-beta.0",
- "@tsparticles/path-spiral": "workspace:4.0.0-beta.0",
- "@tsparticles/path-svg": "workspace:4.0.0-beta.0",
- "@tsparticles/path-utils": "workspace:4.0.0-beta.0",
- "@tsparticles/path-zig-zag": "workspace:4.0.0-beta.0",
- "@tsparticles/perlin-noise": "workspace:4.0.0-beta.0",
- "@tsparticles/pjs": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-absorbers": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-background-mask": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-blend": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-canvas-mask": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-easing-back": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-easing-bounce": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-easing-circ": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-easing-cubic": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-easing-elastic": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-easing-expo": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-easing-gaussian": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-easing-linear": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-easing-quad": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-easing-quart": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-easing-quint": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-easing-sigmoid": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-easing-sine": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-easing-smoothstep": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-emitters": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-emitters-shape-canvas": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-emitters-shape-circle": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-emitters-shape-path": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-emitters-shape-polygon": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-emitters-shape-square": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-export-image": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-export-json": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-export-video": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-hex-color": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-hsl-color": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-hsv-color": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-hwb-color": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-infection": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-interactivity": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-lab-color": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-lch-color": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-manual-particles": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-motion": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-move": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-named-color": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-oklab-color": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-oklch-color": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-poisson-disc": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-polygon-mask": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-responsive": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-rgb-color": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-sounds": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-themes": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-trail": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-zoom": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-arrow": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-cards": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-circle": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-cog": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-emoji": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-heart": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-image": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-infinity": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-line": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-matrix": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-path": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-polygon": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-rounded-polygon": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-rounded-rect": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-spiral": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-square": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-squircle": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-star": "workspace:4.0.0-beta.0",
- "@tsparticles/shape-text": "workspace:4.0.0-beta.0",
- "@tsparticles/simplex-noise": "workspace:4.0.0-beta.0",
- "@tsparticles/slim": "workspace:4.0.0-beta.0",
- "@tsparticles/smooth-value-noise": "workspace:4.0.0-beta.0",
- "@tsparticles/updater-destroy": "workspace:4.0.0-beta.0",
- "@tsparticles/updater-fill-color": "workspace:4.0.0-beta.0",
- "@tsparticles/updater-gradient": "workspace:4.0.0-beta.0",
- "@tsparticles/updater-life": "workspace:4.0.0-beta.0",
- "@tsparticles/updater-opacity": "workspace:4.0.0-beta.0",
- "@tsparticles/updater-orbit": "workspace:4.0.0-beta.0",
- "@tsparticles/updater-out-modes": "workspace:4.0.0-beta.0",
- "@tsparticles/updater-roll": "workspace:4.0.0-beta.0",
- "@tsparticles/updater-rotate": "workspace:4.0.0-beta.0",
- "@tsparticles/updater-size": "workspace:4.0.0-beta.0",
- "@tsparticles/updater-stroke-color": "workspace:4.0.0-beta.0",
- "@tsparticles/updater-tilt": "workspace:4.0.0-beta.0",
- "@tsparticles/updater-twinkle": "workspace:4.0.0-beta.0",
- "@tsparticles/updater-wobble": "workspace:4.0.0-beta.0",
- "tsparticles": "workspace:4.0.0-beta.0"
+ "@tsparticles/all": "workspace:4.0.0-beta.1",
+ "@tsparticles/basic": "workspace:4.0.0-beta.1",
+ "@tsparticles/canvas-utils": "workspace:4.0.0-beta.1",
+ "@tsparticles/confetti": "workspace:4.0.0-beta.1",
+ "@tsparticles/configs": "workspace:4.0.0-beta.1",
+ "@tsparticles/effect-bubble": "workspace:4.0.0-beta.1",
+ "@tsparticles/effect-particles": "workspace:4.0.0-beta.1",
+ "@tsparticles/effect-shadow": "workspace:4.0.0-beta.1",
+ "@tsparticles/effect-trail": "workspace:4.0.0-beta.1",
+ "@tsparticles/engine": "workspace:4.0.0-beta.1",
+ "@tsparticles/fireworks": "workspace:4.0.0-beta.1",
+ "@tsparticles/fractal-noise": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-external-attract": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-external-bounce": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-external-bubble": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-external-cannon": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-external-connect": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-external-drag": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-external-grab": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-external-parallax": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-external-particle": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-external-pause": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-external-pop": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-external-push": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-external-remove": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-external-repulse": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-external-slow": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-external-trail": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-light": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-particles-attract": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-particles-collisions": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-particles-links": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-particles-repulse": "workspace:4.0.0-beta.1",
+ "@tsparticles/noise-field": "workspace:4.0.0-beta.1",
+ "@tsparticles/path-branches": "workspace:4.0.0-beta.1",
+ "@tsparticles/path-brownian": "workspace:4.0.0-beta.1",
+ "@tsparticles/path-curl-noise": "workspace:4.0.0-beta.1",
+ "@tsparticles/path-curves": "workspace:4.0.0-beta.1",
+ "@tsparticles/path-fractal-noise": "workspace:4.0.0-beta.1",
+ "@tsparticles/path-grid": "workspace:4.0.0-beta.1",
+ "@tsparticles/path-levy": "workspace:4.0.0-beta.1",
+ "@tsparticles/path-perlin-noise": "workspace:4.0.0-beta.1",
+ "@tsparticles/path-polygon": "workspace:4.0.0-beta.1",
+ "@tsparticles/path-random": "workspace:4.0.0-beta.1",
+ "@tsparticles/path-simplex-noise": "workspace:4.0.0-beta.1",
+ "@tsparticles/path-spiral": "workspace:4.0.0-beta.1",
+ "@tsparticles/path-svg": "workspace:4.0.0-beta.1",
+ "@tsparticles/path-utils": "workspace:4.0.0-beta.1",
+ "@tsparticles/path-zig-zag": "workspace:4.0.0-beta.1",
+ "@tsparticles/perlin-noise": "workspace:4.0.0-beta.1",
+ "@tsparticles/pjs": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-absorbers": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-background-mask": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-blend": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-canvas-mask": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-easing-back": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-easing-bounce": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-easing-circ": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-easing-cubic": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-easing-elastic": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-easing-expo": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-easing-gaussian": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-easing-linear": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-easing-quad": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-easing-quart": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-easing-quint": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-easing-sigmoid": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-easing-sine": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-easing-smoothstep": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-emitters": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-emitters-shape-canvas": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-emitters-shape-circle": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-emitters-shape-path": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-emitters-shape-polygon": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-emitters-shape-square": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-export-image": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-export-json": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-export-video": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-hex-color": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-hsl-color": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-hsv-color": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-hwb-color": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-infection": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-interactivity": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-lab-color": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-lch-color": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-manual-particles": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-motion": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-move": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-named-color": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-oklab-color": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-oklch-color": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-poisson-disc": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-polygon-mask": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-responsive": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-rgb-color": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-sounds": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-themes": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-trail": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-zoom": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-arrow": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-cards": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-circle": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-cog": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-emoji": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-heart": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-image": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-infinity": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-line": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-matrix": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-path": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-polygon": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-rounded-polygon": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-rounded-rect": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-spiral": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-square": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-squircle": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-star": "workspace:4.0.0-beta.1",
+ "@tsparticles/shape-text": "workspace:4.0.0-beta.1",
+ "@tsparticles/simplex-noise": "workspace:4.0.0-beta.1",
+ "@tsparticles/slim": "workspace:4.0.0-beta.1",
+ "@tsparticles/smooth-value-noise": "workspace:4.0.0-beta.1",
+ "@tsparticles/updater-destroy": "workspace:4.0.0-beta.1",
+ "@tsparticles/updater-fill-color": "workspace:4.0.0-beta.1",
+ "@tsparticles/updater-gradient": "workspace:4.0.0-beta.1",
+ "@tsparticles/updater-life": "workspace:4.0.0-beta.1",
+ "@tsparticles/updater-opacity": "workspace:4.0.0-beta.1",
+ "@tsparticles/updater-orbit": "workspace:4.0.0-beta.1",
+ "@tsparticles/updater-out-modes": "workspace:4.0.0-beta.1",
+ "@tsparticles/updater-roll": "workspace:4.0.0-beta.1",
+ "@tsparticles/updater-rotate": "workspace:4.0.0-beta.1",
+ "@tsparticles/updater-size": "workspace:4.0.0-beta.1",
+ "@tsparticles/updater-stroke-color": "workspace:4.0.0-beta.1",
+ "@tsparticles/updater-tilt": "workspace:4.0.0-beta.1",
+ "@tsparticles/updater-twinkle": "workspace:4.0.0-beta.1",
+ "@tsparticles/updater-wobble": "workspace:4.0.0-beta.1",
+ "tsparticles": "workspace:4.0.0-beta.1"
},
"type": "module"
}
diff --git a/demo/vanilla/public/javascripts/demo.js b/demo/vanilla/public/javascripts/demo.js
index 0401173fce4..8b8ae250c79 100644
--- a/demo/vanilla/public/javascripts/demo.js
+++ b/demo/vanilla/public/javascripts/demo.js
@@ -93,7 +93,10 @@
});
}
- const particles = await tsParticles.load({ id: "tsparticles", options: tsParticles.configs[presetId] });
+ const particles = await tsParticles.load({
+ id: "tsparticles",
+ options: tsParticles.pluginManager.configs[presetId],
+ });
localStorage.presetId = presetId;
@@ -165,14 +168,14 @@
document.addEventListener("DOMContentLoaded", async () => {
await initParticles(tsParticles);
- for (const presetId in tsParticles.configs) {
- const preset = tsParticles.configs[presetId];
+ for (const presetId in tsParticles.pluginManager.configs) {
+ const preset = tsParticles.pluginManager.configs[presetId];
- const option = document.createElement("option");
- option.value = presetId;
- option.text = preset.name || presetId;
+ const option = document.createElement("option");
+ option.value = presetId;
+ option.text = preset.name || presetId;
- document.getElementById("presets").appendChild(option);
+ document.getElementById("presets").appendChild(option);
}
const element = document.getElementById("editor"), options = {
diff --git a/demo/vanilla/views/index.pug b/demo/vanilla/views/index.pug
index 635288cef70..b27e4d04631 100644
--- a/demo/vanilla/views/index.pug
+++ b/demo/vanilla/views/index.pug
@@ -128,6 +128,7 @@ html(lang="en")
script(src="/interaction-external-attract/tsparticles.interaction.external.attract.min.js")
script(src="/interaction-external-bounce/tsparticles.interaction.external.bounce.min.js")
script(src="/interaction-external-bubble/tsparticles.interaction.external.bubble.min.js")
+ script(src="/interaction-external-cannon/tsparticles.interaction.external.cannon.min.js")
script(src="/interaction-external-connect/tsparticles.interaction.external.connect.min.js")
script(src="/interaction-external-drag/tsparticles.interaction.external.drag.min.js")
script(src="/interaction-external-grab/tsparticles.interaction.external.grab.min.js")
diff --git a/demo/vanilla_new/CHANGELOG.md b/demo/vanilla_new/CHANGELOG.md
index fb72392b2d4..96b9d5259ad 100644
--- a/demo/vanilla_new/CHANGELOG.md
+++ b/demo/vanilla_new/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [4.0.0-beta.1](https://github.com/tsparticles/tsparticles/compare/v4.0.0-beta.0...v4.0.0-beta.1) (2026-03-31)
+
+**Note:** Version bump only for package @tsparticles/demo-new
+
# [4.0.0-beta.0](https://github.com/tsparticles/tsparticles/compare/v4.0.0-alpha.28...v4.0.0-beta.0) (2026-03-19)
**Note:** Version bump only for package @tsparticles/demo-new
diff --git a/demo/vanilla_new/package.json b/demo/vanilla_new/package.json
index b43cc393f38..586bd6ced2a 100644
--- a/demo/vanilla_new/package.json
+++ b/demo/vanilla_new/package.json
@@ -1,7 +1,7 @@
{
"name": "@tsparticles/demo-new",
"private": true,
- "version": "4.0.0-beta.0",
+ "version": "4.0.0-beta.1",
"description": "tsParticles Demo Website",
"main": "index.html",
"scripts": {
@@ -40,18 +40,18 @@
"sass": "^1.98.0"
},
"dependencies": {
- "@tsparticles/configs": "workspace:4.0.0-beta.0",
- "@tsparticles/engine": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-light": "workspace:4.0.0-beta.0",
- "@tsparticles/interaction-particles-repulse": "workspace:4.0.0-beta.0",
- "@tsparticles/path-curves": "workspace:4.0.0-beta.0",
- "@tsparticles/path-perlin-noise": "workspace:4.0.0-beta.0",
- "@tsparticles/path-polygon": "workspace:4.0.0-beta.0",
- "@tsparticles/path-simplex-noise": "workspace:4.0.0-beta.0",
- "@tsparticles/plugin-infection": "workspace:4.0.0-beta.0",
- "@tsparticles/updater-gradient": "workspace:4.0.0-beta.0",
- "@tsparticles/updater-orbit": "workspace:4.0.0-beta.0",
- "tsparticles": "workspace:4.0.0-beta.0"
+ "@tsparticles/configs": "workspace:4.0.0-beta.1",
+ "@tsparticles/engine": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-light": "workspace:4.0.0-beta.1",
+ "@tsparticles/interaction-particles-repulse": "workspace:4.0.0-beta.1",
+ "@tsparticles/path-curves": "workspace:4.0.0-beta.1",
+ "@tsparticles/path-perlin-noise": "workspace:4.0.0-beta.1",
+ "@tsparticles/path-polygon": "workspace:4.0.0-beta.1",
+ "@tsparticles/path-simplex-noise": "workspace:4.0.0-beta.1",
+ "@tsparticles/plugin-infection": "workspace:4.0.0-beta.1",
+ "@tsparticles/updater-gradient": "workspace:4.0.0-beta.1",
+ "@tsparticles/updater-orbit": "workspace:4.0.0-beta.1",
+ "tsparticles": "workspace:4.0.0-beta.1"
},
"type": "module"
}
diff --git a/demo/vite/CHANGELOG.md b/demo/vite/CHANGELOG.md
index 608e9712c01..35f4eb20452 100644
--- a/demo/vite/CHANGELOG.md
+++ b/demo/vite/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [4.0.0-beta.1](https://github.com/tsparticles/tsparticles/compare/v4.0.0-beta.0...v4.0.0-beta.1) (2026-03-31)
+
+**Note:** Version bump only for package @tsparticles/vite-demo
+
# [4.0.0-beta.0](https://github.com/tsparticles/tsparticles/compare/v4.0.0-alpha.28...v4.0.0-beta.0) (2026-03-19)
**Note:** Version bump only for package @tsparticles/vite-demo
diff --git a/demo/vite/package.json b/demo/vite/package.json
index a3447c4c3df..ff900259e70 100644
--- a/demo/vite/package.json
+++ b/demo/vite/package.json
@@ -1,7 +1,7 @@
{
"name": "@tsparticles/vite-demo",
"private": true,
- "version": "4.0.0-beta.0",
+ "version": "4.0.0-beta.1",
"type": "module",
"scripts": {
"dev": "vite",
@@ -9,9 +9,9 @@
"preview": "vite preview"
},
"dependencies": {
- "@tsparticles/all": "workspace:4.0.0-beta.0",
- "@tsparticles/configs": "workspace:4.0.0-beta.0",
- "@tsparticles/engine": "workspace:4.0.0-beta.0"
+ "@tsparticles/all": "workspace:4.0.0-beta.1",
+ "@tsparticles/configs": "workspace:4.0.0-beta.1",
+ "@tsparticles/engine": "workspace:4.0.0-beta.1"
},
"devDependencies": {
"typescript": "^5.9.3",
diff --git a/effects/bubble/CHANGELOG.md b/effects/bubble/CHANGELOG.md
index 94fe37a59ca..e7a15d5a105 100644
--- a/effects/bubble/CHANGELOG.md
+++ b/effects/bubble/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [4.0.0-beta.1](https://github.com/tsparticles/tsparticles/compare/v4.0.0-beta.0...v4.0.0-beta.1) (2026-03-31)
+
+**Note:** Version bump only for package @tsparticles/effect-bubble
+
# [4.0.0-beta.0](https://github.com/tsparticles/tsparticles/compare/v4.0.0-alpha.28...v4.0.0-beta.0) (2026-03-19)
**Note:** Version bump only for package @tsparticles/effect-bubble
diff --git a/effects/bubble/package.dist.json b/effects/bubble/package.dist.json
index fec9ddbbeda..8913bea9b75 100644
--- a/effects/bubble/package.dist.json
+++ b/effects/bubble/package.dist.json
@@ -1,6 +1,6 @@
{
"name": "@tsparticles/effect-bubble",
- "version": "4.0.0-beta.0",
+ "version": "4.0.0-beta.1",
"description": "tsParticles bubble effect",
"homepage": "https://particles.js.org",
"repository": {
@@ -100,7 +100,7 @@
"./package.json": "./package.json"
},
"dependencies": {
- "@tsparticles/engine": "4.0.0-beta.0"
+ "@tsparticles/engine": "4.0.0-beta.1"
},
"publishConfig": {
"access": "public"
diff --git a/effects/bubble/package.json b/effects/bubble/package.json
index e3356d982f1..1167b2c25e4 100644
--- a/effects/bubble/package.json
+++ b/effects/bubble/package.json
@@ -1,6 +1,6 @@
{
"name": "@tsparticles/effect-bubble",
- "version": "4.0.0-beta.0",
+ "version": "4.0.0-beta.1",
"description": "tsParticles bubble effect",
"homepage": "https://particles.js.org",
"scripts": {
@@ -108,7 +108,7 @@
"./package.json": "./dist/package.json"
},
"dependencies": {
- "@tsparticles/engine": "workspace:4.0.0-beta.0"
+ "@tsparticles/engine": "workspace:4.0.0-beta.1"
},
"publishConfig": {
"access": "public",
diff --git a/effects/bubble/src/index.ts b/effects/bubble/src/index.ts
index 563498ee244..4e1dca7b1f1 100644
--- a/effects/bubble/src/index.ts
+++ b/effects/bubble/src/index.ts
@@ -8,8 +8,8 @@ declare const __VERSION__: string;
export async function loadBubbleEffect(engine: Engine): Promise {
engine.checkVersion(__VERSION__);
- await engine.register(e => {
- e.addEffect("bubble", async () => {
+ await engine.pluginManager.register(e => {
+ e.pluginManager.addEffect("bubble", async () => {
const { BubbleDrawer } = await import("./BubbleDrawer.js");
return new BubbleDrawer();
diff --git a/effects/particles/CHANGELOG.md b/effects/particles/CHANGELOG.md
index e43bbb694d6..e424af96c0f 100644
--- a/effects/particles/CHANGELOG.md
+++ b/effects/particles/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [4.0.0-beta.1](https://github.com/tsparticles/tsparticles/compare/v4.0.0-beta.0...v4.0.0-beta.1) (2026-03-31)
+
+**Note:** Version bump only for package @tsparticles/effect-particles
+
# [4.0.0-beta.0](https://github.com/tsparticles/tsparticles/compare/v4.0.0-alpha.28...v4.0.0-beta.0) (2026-03-19)
**Note:** Version bump only for package @tsparticles/effect-particles
diff --git a/effects/particles/package.dist.json b/effects/particles/package.dist.json
index ab9123f055e..ed6a631d31d 100644
--- a/effects/particles/package.dist.json
+++ b/effects/particles/package.dist.json
@@ -1,6 +1,6 @@
{
"name": "@tsparticles/effect-particles",
- "version": "4.0.0-beta.0",
+ "version": "4.0.0-beta.1",
"description": "tsParticles particles effect",
"homepage": "https://particles.js.org",
"repository": {
@@ -100,7 +100,7 @@
"./package.json": "./package.json"
},
"dependencies": {
- "@tsparticles/engine": "4.0.0-beta.0"
+ "@tsparticles/engine": "4.0.0-beta.1"
},
"publishConfig": {
"access": "public"
diff --git a/effects/particles/package.json b/effects/particles/package.json
index f4f18186f85..08fc361b620 100644
--- a/effects/particles/package.json
+++ b/effects/particles/package.json
@@ -1,6 +1,6 @@
{
"name": "@tsparticles/effect-particles",
- "version": "4.0.0-beta.0",
+ "version": "4.0.0-beta.1",
"description": "tsParticles particles effect",
"homepage": "https://particles.js.org",
"scripts": {
@@ -108,7 +108,7 @@
"./package.json": "./dist/package.json"
},
"dependencies": {
- "@tsparticles/engine": "workspace:4.0.0-beta.0"
+ "@tsparticles/engine": "workspace:4.0.0-beta.1"
},
"publishConfig": {
"access": "public",
diff --git a/effects/particles/src/ParticlesDrawer.ts b/effects/particles/src/ParticlesDrawer.ts
index 46789a668b9..46ed6b90e58 100644
--- a/effects/particles/src/ParticlesDrawer.ts
+++ b/effects/particles/src/ParticlesDrawer.ts
@@ -38,9 +38,15 @@ type ParticlesParticle = Particle & {
};
export class ParticlesDrawer implements IEffectDrawer {
+ private readonly _container;
+
+ constructor(container: Container) {
+ this._container = container;
+ }
+
drawAfter(data: IShapeDrawData): void {
const { particle } = data,
- { container } = particle;
+ { _container: container } = this;
if (!particle.particlesNextSpawn) {
return;
diff --git a/effects/particles/src/index.ts b/effects/particles/src/index.ts
index 9a8ea328173..51eb7df912a 100644
--- a/effects/particles/src/index.ts
+++ b/effects/particles/src/index.ts
@@ -8,11 +8,11 @@ declare const __VERSION__: string;
export async function loadParticlesEffect(engine: Engine): Promise {
engine.checkVersion(__VERSION__);
- await engine.register(e => {
- e.addEffect("particles", async () => {
+ await engine.pluginManager.register(e => {
+ e.pluginManager.addEffect("particles", async container => {
const { ParticlesDrawer } = await import("./ParticlesDrawer.js");
- return new ParticlesDrawer();
+ return new ParticlesDrawer(container);
});
});
}
diff --git a/effects/shadow/CHANGELOG.md b/effects/shadow/CHANGELOG.md
index eaa45845a6f..6ce812fee7c 100644
--- a/effects/shadow/CHANGELOG.md
+++ b/effects/shadow/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [4.0.0-beta.1](https://github.com/tsparticles/tsparticles/compare/v4.0.0-beta.0...v4.0.0-beta.1) (2026-03-31)
+
+**Note:** Version bump only for package @tsparticles/effect-shadow
+
# [4.0.0-beta.0](https://github.com/tsparticles/tsparticles/compare/v4.0.0-alpha.28...v4.0.0-beta.0) (2026-03-19)
**Note:** Version bump only for package @tsparticles/effect-shadow
diff --git a/effects/shadow/package.dist.json b/effects/shadow/package.dist.json
index 2d326fcd679..4899babdc1c 100644
--- a/effects/shadow/package.dist.json
+++ b/effects/shadow/package.dist.json
@@ -1,6 +1,6 @@
{
"name": "@tsparticles/effect-shadow",
- "version": "4.0.0-beta.0",
+ "version": "4.0.0-beta.1",
"description": "tsParticles shadow effect",
"homepage": "https://particles.js.org",
"repository": {
@@ -100,7 +100,7 @@
"./package.json": "./package.json"
},
"dependencies": {
- "@tsparticles/engine": "4.0.0-beta.0"
+ "@tsparticles/engine": "4.0.0-beta.1"
},
"publishConfig": {
"access": "public"
diff --git a/effects/shadow/package.json b/effects/shadow/package.json
index 2da62494812..699aaea10a9 100644
--- a/effects/shadow/package.json
+++ b/effects/shadow/package.json
@@ -1,6 +1,6 @@
{
"name": "@tsparticles/effect-shadow",
- "version": "4.0.0-beta.0",
+ "version": "4.0.0-beta.1",
"description": "tsParticles shadow effect",
"homepage": "https://particles.js.org",
"scripts": {
@@ -108,7 +108,7 @@
"./package.json": "./dist/package.json"
},
"dependencies": {
- "@tsparticles/engine": "workspace:4.0.0-beta.0"
+ "@tsparticles/engine": "workspace:4.0.0-beta.1"
},
"publishConfig": {
"access": "public",
diff --git a/effects/shadow/src/ShadowDrawer.ts b/effects/shadow/src/ShadowDrawer.ts
index 1d29e9bfa3e..9b8053f0edd 100644
--- a/effects/shadow/src/ShadowDrawer.ts
+++ b/effects/shadow/src/ShadowDrawer.ts
@@ -1,6 +1,5 @@
import {
type Container,
- type Engine,
type ICoordinates,
type IEffectDrawer,
type IOptionsColor,
@@ -9,6 +8,7 @@ import {
type IShapeValues,
OptionsColor,
type Particle,
+ type PluginManager,
getStyleFromRgb,
originPoint,
rangeColorToRgb,
@@ -30,10 +30,12 @@ type ShadowParticle = Particle & {
};
export class ShadowDrawer implements IEffectDrawer {
- private readonly _engine: Engine;
+ private readonly _container;
+ private readonly _pluginManager;
- constructor(engine: Engine) {
- this._engine = engine;
+ constructor(pluginManager: PluginManager, container: Container) {
+ this._pluginManager = pluginManager;
+ this._container = container;
}
drawAfter(data: IShapeDrawData): void {
@@ -47,7 +49,7 @@ export class ShadowDrawer implements IEffectDrawer {
drawBefore(data: IShapeDrawData): void {
const { particle, context } = data,
- { container } = particle,
+ { _container: container } = this,
shadowParticle = particle as ShadowParticle,
shadowColor = shadowParticle.shadowColor,
shadowOffset = shadowParticle.shadowOffset;
@@ -66,7 +68,7 @@ export class ShadowDrawer implements IEffectDrawer {
const effectData = particle.effectData as IShadowData | undefined,
shadowColor = OptionsColor.create(new OptionsColor(), effectData?.color);
- particle.shadowColor = rangeColorToRgb(this._engine, shadowColor);
+ particle.shadowColor = rangeColorToRgb(this._pluginManager, shadowColor);
particle.shadowBlur = effectData?.blur ?? defaultShadowBlur;
particle.shadowOffset = effectData?.offset ?? originPoint;
}
diff --git a/effects/shadow/src/index.ts b/effects/shadow/src/index.ts
index ccff5e6affc..4d176611327 100644
--- a/effects/shadow/src/index.ts
+++ b/effects/shadow/src/index.ts
@@ -8,11 +8,11 @@ declare const __VERSION__: string;
export async function loadShadowEffect(engine: Engine): Promise {
engine.checkVersion(__VERSION__);
- await engine.register(e => {
- e.addEffect("shadow", async () => {
+ await engine.pluginManager.register(e => {
+ e.pluginManager.addEffect("shadow", async container => {
const { ShadowDrawer } = await import("./ShadowDrawer.js");
- return new ShadowDrawer(e);
+ return new ShadowDrawer(e.pluginManager, container);
});
});
}
diff --git a/effects/trail/CHANGELOG.md b/effects/trail/CHANGELOG.md
index f39ef93e4ff..91f8665392e 100644
--- a/effects/trail/CHANGELOG.md
+++ b/effects/trail/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [4.0.0-beta.1](https://github.com/tsparticles/tsparticles/compare/v4.0.0-beta.0...v4.0.0-beta.1) (2026-03-31)
+
+**Note:** Version bump only for package @tsparticles/effect-trail
+
# [4.0.0-beta.0](https://github.com/tsparticles/tsparticles/compare/v4.0.0-alpha.28...v4.0.0-beta.0) (2026-03-19)
### Bug Fixes
diff --git a/effects/trail/package.dist.json b/effects/trail/package.dist.json
index e1450c75231..e5de668061f 100644
--- a/effects/trail/package.dist.json
+++ b/effects/trail/package.dist.json
@@ -1,6 +1,6 @@
{
"name": "@tsparticles/effect-trail",
- "version": "4.0.0-beta.0",
+ "version": "4.0.0-beta.1",
"description": "tsParticles trail effect",
"homepage": "https://particles.js.org",
"repository": {
@@ -100,7 +100,7 @@
"./package.json": "./package.json"
},
"dependencies": {
- "@tsparticles/engine": "4.0.0-beta.0"
+ "@tsparticles/engine": "4.0.0-beta.1"
},
"publishConfig": {
"access": "public"
diff --git a/effects/trail/package.json b/effects/trail/package.json
index a1c25fdbd64..76520762c79 100644
--- a/effects/trail/package.json
+++ b/effects/trail/package.json
@@ -1,6 +1,6 @@
{
"name": "@tsparticles/effect-trail",
- "version": "4.0.0-beta.0",
+ "version": "4.0.0-beta.1",
"description": "tsParticles trail effect",
"homepage": "https://particles.js.org",
"scripts": {
@@ -108,7 +108,7 @@
"./package.json": "./dist/package.json"
},
"dependencies": {
- "@tsparticles/engine": "workspace:4.0.0-beta.0"
+ "@tsparticles/engine": "workspace:4.0.0-beta.1"
},
"publishConfig": {
"access": "public",
diff --git a/effects/trail/src/TrailDrawer.ts b/effects/trail/src/TrailDrawer.ts
index 71641f4f21d..c6c5dd88a3e 100644
--- a/effects/trail/src/TrailDrawer.ts
+++ b/effects/trail/src/TrailDrawer.ts
@@ -58,10 +58,17 @@ const defaultTransform = {
};
export class TrailDrawer implements IEffectDrawer {
+ private readonly _container;
+
+ constructor(container: Container) {
+ this._container = container;
+ }
+
drawAfter(data: IShapeDrawData): void {
const { context, drawPosition, drawRadius, drawScale, particle, transformData } = data,
+ container = this._container,
diameter = drawRadius * double,
- pxRatio = particle.container.retina.pixelRatio,
+ pxRatio = container.retina.pixelRatio,
trail = particle.trail;
if (!trail || !particle.trailLength) {
@@ -92,8 +99,8 @@ export class TrailDrawer implements IEffectDrawer {
const trailLength = Math.min(trail.length, pathLength),
canvasSize = {
- width: particle.container.canvas.size.width * drawScale + diameter,
- height: particle.container.canvas.size.height * drawScale + diameter,
+ width: container.canvas.size.width * drawScale + diameter,
+ height: container.canvas.size.height * drawScale + diameter,
};
context.save();
diff --git a/effects/trail/src/index.ts b/effects/trail/src/index.ts
index 1918809047b..1aad9d25246 100644
--- a/effects/trail/src/index.ts
+++ b/effects/trail/src/index.ts
@@ -8,11 +8,11 @@ declare const __VERSION__: string;
export async function loadTrailEffect(engine: Engine): Promise {
engine.checkVersion(__VERSION__);
- await engine.register(e => {
- e.addEffect("trail", async () => {
+ await engine.pluginManager.register(e => {
+ e.pluginManager.addEffect("trail", async container => {
const { TrailDrawer } = await import("./TrailDrawer.js");
- return new TrailDrawer();
+ return new TrailDrawer(container);
});
});
}
diff --git a/engine/CHANGELOG.md b/engine/CHANGELOG.md
index 627a9a4a10a..e7d5761d345 100644
--- a/engine/CHANGELOG.md
+++ b/engine/CHANGELOG.md
@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [4.0.0-beta.1](https://github.com/tsparticles/tsparticles/compare/v4.0.0-beta.0...v4.0.0-beta.1) (2026-03-31)
+
+### Bug Fixes
+
+- improved cannon and interactivity events ([b836a59](https://github.com/tsparticles/tsparticles/commit/b836a59e90afdc68b6307e8d37898e71b5881a21))
+
# [4.0.0-beta.0](https://github.com/tsparticles/tsparticles/compare/v4.0.0-alpha.28...v4.0.0-beta.0) (2026-03-19)
**Note:** Version bump only for package @tsparticles/engine
diff --git a/engine/README.md b/engine/README.md
index 0bf9af293a0..1f0f2dde239 100644
--- a/engine/README.md
+++ b/engine/README.md
@@ -98,8 +98,6 @@ React.js, Vue.js (2.x and 3.x), Angular, Svelte, jQuery, Preact, Inferno, Riot.j
- [Migrating from Particles.js](#migrating-from-particlesjs)
- [Plugins/Customizations](#pluginscustomizations)
- [Dependency Graph](#dependency-graph)
-- [Sponsors](#sponsors)
-- [Huge thanks to JetBrains for the 2020-2022 Open Source Licenses!](#huge-thanks-to-jetbrains-for-the-2020-2022-open-source-licenses)
---
@@ -989,22 +987,3 @@ flowchart TD
bundle-full --> bundle-all
```
-
----
-
-
-
-
-
-
-
-
-
-
-## Sponsors
-
-### JetBrains
-
-Huge thanks to [JetBrains](https://www.jetbrains.com/?from=tsParticles) for the 2020-2022 Open Source Licenses!
-
-[JetBrains WebStorm](https://www.jetbrains.com/webstorm/?from=tsParticles) is used to maintain this project.
diff --git a/engine/package.dist.json b/engine/package.dist.json
index 0b18fe275ef..4f619c434b8 100644
--- a/engine/package.dist.json
+++ b/engine/package.dist.json
@@ -1,6 +1,6 @@
{
"name": "@tsparticles/engine",
- "version": "4.0.0-beta.0",
+ "version": "4.0.0-beta.1",
"description": "Easily create highly customizable particle, confetti and fireworks animations and use them as animated backgrounds for your website. Ready to use components available also for React, Vue.js (2.x and 3.x), Angular, Svelte, jQuery, Preact, Riot.js, Inferno.",
"homepage": "https://particles.js.org",
"scripts": {
diff --git a/engine/package.json b/engine/package.json
index 9b0ea00b043..7a93fd7cb4f 100644
--- a/engine/package.json
+++ b/engine/package.json
@@ -1,6 +1,6 @@
{
"name": "@tsparticles/engine",
- "version": "4.0.0-beta.0",
+ "version": "4.0.0-beta.1",
"description": "Easily create highly customizable particle, confetti and fireworks animations and use them as animated backgrounds for your website. Ready to use components available also for React, Vue.js (2.x and 3.x), Angular, Svelte, jQuery, Preact, Riot.js, Inferno.",
"homepage": "https://particles.js.org",
"scripts": {
diff --git a/engine/src/Core/Canvas.ts b/engine/src/Core/Canvas.ts
deleted file mode 100644
index 41a3f2e89b4..00000000000
--- a/engine/src/Core/Canvas.ts
+++ /dev/null
@@ -1,879 +0,0 @@
-import { clear, drawParticle, drawParticlePlugin, paintBase, paintImage } from "../Utils/CanvasUtils.js";
-import { cloneStyle, getFullScreenStyle, safeMatchMedia, safeMutationObserver } from "../Utils/Utils.js";
-import {
- defaultCompositeValue,
- defaultTransformValue,
- defaultZoom,
- generatedAttribute,
- half,
- minimumSize,
- zIndexFactorOffset,
-} from "./Utils/Constants.js";
-import { getStyleFromHsl, getStyleFromRgb, rangeColorToHsl, rangeColorToRgb } from "../Utils/ColorUtils.js";
-import type { Container } from "./Container.js";
-import type { Engine } from "./Engine.js";
-import type { IContainerPlugin } from "./Interfaces/IContainerPlugin.js";
-import type { ICoordinates } from "./Interfaces/ICoordinates.js";
-import type { IDelta } from "./Interfaces/IDelta.js";
-import type { IDimension } from "./Interfaces/IDimension.js";
-import type { IHsl } from "./Interfaces/Colors.js";
-import type { IParticleColorStyle } from "./Interfaces/IParticleColorStyle.js";
-import type { IParticleTransformValues } from "./Interfaces/IParticleTransformValues.js";
-import type { IParticleUpdater } from "./Interfaces/IParticleUpdater.js";
-import type { Particle } from "./Particle.js";
-
-const fColorIndex = 0,
- sColorIndex = 1;
-
-/**
- * @param factor -
- * @param newFactor -
- * @param key -
- */
-function setTransformValue(
- factor: Partial,
- newFactor: Partial,
- key: keyof IParticleTransformValues,
-): void {
- const newValue = newFactor[key];
-
- if (newValue !== undefined) {
- factor[key] = (factor[key] ?? defaultTransformValue) * newValue;
- }
-}
-
-/**
- *
- * @param canvas -
- * @param style -
- * @param important -
- */
-function setStyle(canvas: HTMLCanvasElement, style?: CSSStyleDeclaration, important = false): void {
- if (!style) {
- return;
- }
-
- const element = canvas,
- elementStyle = element.style,
- keys = new Set();
-
- for (let i = 0; i < elementStyle.length; i++) {
- const key = elementStyle.item(i);
-
- if (!key) {
- continue;
- }
-
- keys.add(key);
- }
-
- for (let i = 0; i < style.length; i++) {
- const key = style.item(i);
-
- if (!key) {
- continue;
- }
-
- keys.add(key);
- }
-
- for (const key of keys) {
- const value = style.getPropertyValue(key);
-
- if (value) {
- elementStyle.setProperty(key, value, important ? "important" : "");
- } else {
- elementStyle.removeProperty(key);
- }
- }
-}
-
-/**
- * Canvas manager
- */
-export class Canvas {
- /**
- * The particles canvas
- */
- element?: HTMLCanvasElement;
-
- /**
- * The particles canvas dimension
- */
- readonly size: IDimension;
-
- /**
- * Current zoom level
- */
- zoom = defaultZoom;
-
- private _canvasClearPlugins: IContainerPlugin[];
- private _canvasPaintPlugins: IContainerPlugin[];
- private _canvasSettings?: CanvasRenderingContext2DSettings;
- private _clearDrawPlugins: IContainerPlugin[];
- private _colorPlugins: IContainerPlugin[];
- /**
- * The particles canvas context
- */
- private _context: CanvasRenderingContext2D | null;
- private _drawParticlePlugins: IContainerPlugin[];
- private _drawParticlesCleanupPlugins: IContainerPlugin[];
- private _drawParticlesSetupPlugins: IContainerPlugin[];
- private _drawPlugins: IContainerPlugin[];
- private _drawSettingsCleanupPlugins: IContainerPlugin[];
- private _drawSettingsSetupPlugins: IContainerPlugin[];
- private readonly _engine;
- private _generated;
- private _mutationObserver?: MutationObserver;
- private _originalStyle?: CSSStyleDeclaration;
- private _pointerEvents: string;
- private _postDrawUpdaters: IParticleUpdater[];
- private _preDrawUpdaters: IParticleUpdater[];
- private _resizePlugins: IContainerPlugin[];
- private readonly _reusableColorStyles: IParticleColorStyle = {};
- private readonly _reusablePluginColors: (IHsl | undefined)[] = [undefined, undefined];
- private readonly _reusableTransform: Partial = {};
- private readonly _standardSize: IDimension;
-
- /**
- * Zoom center point (for centered zooming)
- */
- private _zoomCenter?: ICoordinates;
-
- /**
- * Constructor of canvas manager
- * @param container - the parent container
- * @param engine - the engine managing the whole library
- */
- constructor(
- private readonly container: Container,
- engine: Engine,
- ) {
- this._engine = engine;
- this._standardSize = {
- height: 0,
- width: 0,
- };
-
- const pxRatio = container.retina.pixelRatio,
- stdSize = this._standardSize;
-
- this.size = {
- height: stdSize.height * pxRatio,
- width: stdSize.width * pxRatio,
- };
-
- this._context = null;
- this._generated = false;
- this._preDrawUpdaters = [];
- this._postDrawUpdaters = [];
- this._resizePlugins = [];
- this._colorPlugins = [];
- this._canvasClearPlugins = [];
- this._canvasPaintPlugins = [];
- this._clearDrawPlugins = [];
- this._drawParticlePlugins = [];
- this._drawParticlesCleanupPlugins = [];
- this._drawParticlesSetupPlugins = [];
- this._drawPlugins = [];
- this._drawSettingsSetupPlugins = [];
- this._drawSettingsCleanupPlugins = [];
- this._pointerEvents = "none";
- }
-
- get settings(): CanvasRenderingContext2DSettings | undefined {
- return this._canvasSettings;
- }
-
- private get _fullScreen(): boolean {
- return this.container.actualOptions.fullScreen.enable;
- }
-
- canvasClear(): void {
- if (!this.container.actualOptions.clear) {
- return;
- }
-
- this.draw(ctx => {
- clear(ctx, this.size);
- });
- }
-
- /**
- * Clears the canvas content
- */
- clear(): void {
- let pluginHandled = false;
-
- for (const plugin of this._canvasClearPlugins) {
- pluginHandled = plugin.canvasClear?.() ?? false;
-
- if (pluginHandled) {
- break;
- }
- }
-
- if (pluginHandled) {
- return;
- }
-
- this.canvasClear();
- }
-
- /**
- * Destroying object actions
- */
- destroy(): void {
- this.stop();
-
- if (this._generated) {
- const element = this.element;
-
- element?.remove();
-
- this.element = undefined;
- } else {
- this._resetOriginalStyle();
- }
-
- this._preDrawUpdaters = [];
- this._postDrawUpdaters = [];
- this._resizePlugins = [];
- this._colorPlugins = [];
- this._canvasClearPlugins = [];
- this._canvasPaintPlugins = [];
- this._clearDrawPlugins = [];
- this._drawParticlePlugins = [];
- this._drawParticlesCleanupPlugins = [];
- this._drawParticlesSetupPlugins = [];
- this._drawPlugins = [];
- this._drawSettingsSetupPlugins = [];
- this._drawSettingsCleanupPlugins = [];
- }
-
- /**
- * Generic draw method for drawing stuff on the canvas context
- * @param cb -
- * @returns the result of the callback
- */
- draw(cb: (context: CanvasRenderingContext2D) => T): T | undefined {
- const ctx = this._context;
-
- if (!ctx) {
- return;
- }
-
- return cb(ctx);
- }
-
- /**
- * Draws the specified particle in the canvas
- * @param particle - the particle to draw
- * @param delta - the frame delta time values
- */
- drawParticle(particle: Particle, delta: IDelta): void {
- if (particle.spawning || particle.destroyed) {
- return;
- }
-
- const radius = particle.getRadius();
-
- if (radius <= minimumSize) {
- return;
- }
-
- const pfColor = particle.getFillColor(),
- psColor = particle.getStrokeColor();
-
- let [fColor, sColor] = this._getPluginParticleColors(particle);
-
- fColor ??= pfColor;
- sColor ??= psColor;
-
- if (!fColor && !sColor) {
- return;
- }
-
- const container = this.container,
- zIndexOptions = particle.options.zIndex,
- zIndexFactor = zIndexFactorOffset - particle.zIndexFactor,
- { fillOpacity, opacity, strokeOpacity } = particle.getOpacity(),
- transform = this._reusableTransform,
- colorStyles = this._reusableColorStyles,
- fill = fColor ? getStyleFromHsl(fColor, container.hdr, fillOpacity * opacity) : undefined,
- stroke = sColor ? getStyleFromHsl(sColor, container.hdr, strokeOpacity * opacity) : fill;
-
- transform.a = transform.b = transform.c = transform.d = undefined;
-
- colorStyles.fill = fill;
- colorStyles.stroke = stroke;
-
- this.draw((context): void => {
- for (const plugin of this._drawParticlesSetupPlugins) {
- plugin.drawParticleSetup?.(context, particle, delta);
- }
-
- this._applyPreDrawUpdaters(context, particle, radius, opacity, colorStyles, transform);
-
- drawParticle({
- container,
- context,
- particle,
- delta,
- colorStyles,
- radius: radius * zIndexFactor ** zIndexOptions.sizeRate,
- opacity: opacity,
- transform,
- });
-
- this._applyPostDrawUpdaters(particle);
-
- for (const plugin of this._drawParticlesCleanupPlugins) {
- plugin.drawParticleCleanup?.(context, particle, delta);
- }
- });
- }
-
- /**
- * Draws stuff using the given plugin, using the given particle
- * @param particle - the particle used
- * @param delta - the frame delta time values
- */
- drawParticlePlugins(particle: Particle, delta: IDelta): void {
- this.draw(ctx => {
- for (const plugin of this._drawParticlePlugins) {
- drawParticlePlugin(ctx, plugin, particle, delta);
- }
- });
- }
-
- drawParticles(delta: IDelta): void {
- const { particles } = this.container;
-
- this.clear();
-
- /* update each particle before drawing */
- particles.update(delta);
-
- this.draw(ctx => {
- for (const plugin of this._drawSettingsSetupPlugins) {
- plugin.drawSettingsSetup?.(ctx, delta);
- }
-
- for (const plugin of this._drawPlugins) {
- plugin.draw?.(ctx, delta);
- }
-
- particles.drawParticles(delta);
-
- for (const plugin of this._clearDrawPlugins) {
- plugin.clearDraw?.(ctx, delta);
- }
-
- for (const plugin of this._drawSettingsCleanupPlugins) {
- plugin.drawSettingsCleanup?.(ctx, delta);
- }
- });
- }
-
- getZoomCenter(): ICoordinates {
- const pxRatio = this.container.retina.pixelRatio,
- { width, height } = this.size;
-
- if (this._zoomCenter) {
- return this._zoomCenter;
- }
-
- return {
- x: (width * half) / pxRatio,
- y: (height * half) / pxRatio,
- };
- }
-
- /**
- * Initializes the canvas element
- */
- init(): void {
- this._safeMutationObserver(obs => {
- obs.disconnect();
- });
- this._mutationObserver = safeMutationObserver(records => {
- for (const record of records) {
- if (record.type === "attributes" && record.attributeName === "style") {
- this._repairStyle();
- }
- }
- });
-
- this.resize();
- this._initStyle();
- this.initBackground();
- this._safeMutationObserver(obs => {
- if (!this.element || !(this.element instanceof Node)) {
- return;
- }
-
- obs.observe(this.element, { attributes: true });
- });
- this.initUpdaters();
- this.initPlugins();
- this.paint();
- }
-
- /**
- * Initializes the canvas background
- */
- initBackground(): void {
- const { container } = this,
- options = container.actualOptions,
- background = options.background,
- element = this.element;
-
- if (!element) {
- return;
- }
-
- const elementStyle = element.style,
- color = rangeColorToRgb(this._engine, background.color);
-
- if (color) {
- elementStyle.backgroundColor = getStyleFromRgb(color, container.hdr, background.opacity);
- } else {
- elementStyle.backgroundColor = "";
- }
-
- elementStyle.backgroundImage = background.image || "";
- elementStyle.backgroundPosition = background.position || "";
- elementStyle.backgroundRepeat = background.repeat || "";
- elementStyle.backgroundSize = background.size || "";
- }
-
- /**
- * Initializes the plugins needed by canvas
- */
- initPlugins(): void {
- this._resizePlugins = [];
- this._colorPlugins = [];
- this._canvasClearPlugins = [];
- this._canvasPaintPlugins = [];
- this._clearDrawPlugins = [];
- this._drawParticlePlugins = [];
- this._drawParticlesSetupPlugins = [];
- this._drawParticlesCleanupPlugins = [];
- this._drawPlugins = [];
- this._drawSettingsSetupPlugins = [];
- this._drawSettingsCleanupPlugins = [];
-
- for (const plugin of this.container.plugins) {
- if (plugin.resize) {
- this._resizePlugins.push(plugin);
- }
-
- if (plugin.particleFillColor ?? plugin.particleStrokeColor) {
- this._colorPlugins.push(plugin);
- }
-
- if (plugin.canvasClear) {
- this._canvasClearPlugins.push(plugin);
- }
-
- if (plugin.canvasPaint) {
- this._canvasPaintPlugins.push(plugin);
- }
-
- if (plugin.drawParticle) {
- this._drawParticlePlugins.push(plugin);
- }
-
- if (plugin.drawParticleSetup) {
- this._drawParticlesSetupPlugins.push(plugin);
- }
-
- if (plugin.drawParticleCleanup) {
- this._drawParticlesCleanupPlugins.push(plugin);
- }
-
- if (plugin.draw) {
- this._drawPlugins.push(plugin);
- }
-
- if (plugin.drawSettingsSetup) {
- this._drawSettingsSetupPlugins.push(plugin);
- }
-
- if (plugin.drawSettingsCleanup) {
- this._drawSettingsCleanupPlugins.push(plugin);
- }
-
- if (plugin.clearDraw) {
- this._clearDrawPlugins.push(plugin);
- }
- }
- }
-
- /**
- * Initializes the updaters needed by canvas
- */
- initUpdaters(): void {
- this._preDrawUpdaters = [];
- this._postDrawUpdaters = [];
-
- for (const updater of this.container.particles.updaters) {
- if (updater.afterDraw) {
- this._postDrawUpdaters.push(updater);
- }
-
- if (updater.getColorStyles ?? updater.getTransformValues ?? updater.beforeDraw) {
- this._preDrawUpdaters.push(updater);
- }
- }
- }
-
- /**
- * Loads the canvas HTML element
- * @param canvas - the canvas HTML element
- */
- loadCanvas(canvas: HTMLCanvasElement): void {
- if (this._generated && this.element) {
- this.element.remove();
- }
-
- const container = this.container;
-
- this._generated =
- generatedAttribute in canvas.dataset ? canvas.dataset[generatedAttribute] === "true" : this._generated;
- this.element = canvas;
- this.element.ariaHidden = "true";
- this._originalStyle = cloneStyle(this.element.style);
-
- const standardSize = this._standardSize;
-
- standardSize.height = canvas.offsetHeight;
- standardSize.width = canvas.offsetWidth;
-
- const pxRatio = this.container.retina.pixelRatio,
- retinaSize = this.size;
-
- canvas.height = retinaSize.height = standardSize.height * pxRatio;
- canvas.width = retinaSize.width = standardSize.width * pxRatio;
-
- const canSupportHdrQuery = safeMatchMedia("(color-gamut: p3)");
-
- this._canvasSettings = {
- alpha: true,
- colorSpace: canSupportHdrQuery?.matches && container.hdr ? "display-p3" : "srgb",
- desynchronized: true,
- willReadFrequently: false,
- };
- this._context = this.element.getContext("2d", this._canvasSettings);
-
- if (this._context) {
- this._context.globalCompositeOperation = defaultCompositeValue;
- }
-
- this._safeMutationObserver(obs => {
- obs.disconnect();
- });
-
- container.retina.init();
- this.initBackground();
-
- this._safeMutationObserver(obs => {
- if (!this.element || !(this.element instanceof Node)) {
- return;
- }
-
- obs.observe(this.element, { attributes: true });
- });
- }
-
- /**
- * Paints the canvas background
- */
- paint(): void {
- let handled = false;
-
- for (const plugin of this._canvasPaintPlugins) {
- handled = plugin.canvasPaint?.() ?? false;
-
- if (handled) {
- break;
- }
- }
-
- if (handled) {
- return;
- }
-
- this.paintBase();
- }
-
- paintBase(baseColor?: string): void {
- this.draw(ctx => {
- paintBase(ctx, this.size, baseColor);
- });
- }
-
- paintImage(image: HTMLImageElement, opacity: number): void {
- this.draw(ctx => {
- paintImage(ctx, this.size, image, opacity);
- });
- }
-
- /**
- * Calculates the size of the canvas
- * @returns true if the size changed
- */
- resize(): boolean {
- if (!this.element) {
- return false;
- }
-
- const container = this.container,
- currentSize = container.canvas._standardSize,
- newSize = {
- width: this.element.offsetWidth,
- height: this.element.offsetHeight,
- },
- pxRatio = container.retina.pixelRatio,
- retinaSize = {
- width: newSize.width * pxRatio,
- height: newSize.height * pxRatio,
- };
-
- if (
- newSize.height === currentSize.height &&
- newSize.width === currentSize.width &&
- retinaSize.height === this.element.height &&
- retinaSize.width === this.element.width
- ) {
- return false;
- }
-
- const oldSize = { ...currentSize };
-
- currentSize.height = newSize.height;
- currentSize.width = newSize.width;
-
- const canvasSize = this.size;
-
- this.element.width = canvasSize.width = retinaSize.width;
- this.element.height = canvasSize.height = retinaSize.height;
-
- if (this.container.started) {
- container.particles.setResizeFactor({
- width: currentSize.width / oldSize.width,
- height: currentSize.height / oldSize.height,
- });
- }
-
- return true;
- }
-
- setPointerEvents(type: string): void {
- const element = this.element;
-
- if (!element) {
- return;
- }
-
- this._pointerEvents = type;
- this._repairStyle();
- }
-
- /**
- * Sets the zoom level and center point
- * @param zoomLevel - the new zoom level
- * @param center - optional center point for zoom (default is canvas center)
- */
- setZoom(zoomLevel: number, center?: ICoordinates): void {
- this.zoom = zoomLevel;
- this._zoomCenter = center;
- }
-
- stop(): void {
- this._safeMutationObserver(obs => {
- obs.disconnect();
- });
-
- this._mutationObserver = undefined;
-
- this.draw(ctx => {
- clear(ctx, this.size);
- });
- }
-
- /**
- * The window resize event handler
- */
- async windowResize(): Promise {
- if (!this.element || !this.resize()) {
- return;
- }
-
- const container = this.container,
- needsRefresh = container.updateActualOptions();
-
- /* density particles enabled */
- container.particles.setDensity();
-
- this._applyResizePlugins();
-
- if (needsRefresh) {
- await container.refresh();
- }
- }
-
- private readonly _applyPostDrawUpdaters: (particle: Particle) => void = particle => {
- for (const updater of this._postDrawUpdaters) {
- updater.afterDraw?.(particle);
- }
- };
-
- private readonly _applyPreDrawUpdaters: (
- ctx: CanvasRenderingContext2D,
- particle: Particle,
- radius: number,
- zOpacity: number,
- colorStyles: IParticleColorStyle,
- transform: Partial,
- ) => void = (ctx, particle, radius, zOpacity, colorStyles, transform) => {
- for (const updater of this._preDrawUpdaters) {
- if (updater.getColorStyles) {
- const { fill, stroke } = updater.getColorStyles(particle, ctx, radius, zOpacity);
-
- if (fill) {
- colorStyles.fill = fill;
- }
-
- if (stroke) {
- colorStyles.stroke = stroke;
- }
- }
-
- if (updater.getTransformValues) {
- const updaterTransform = updater.getTransformValues(particle);
-
- for (const key in updaterTransform) {
- setTransformValue(transform, updaterTransform, key as keyof IParticleTransformValues);
- }
- }
-
- updater.beforeDraw?.(particle);
- }
- };
-
- private readonly _applyResizePlugins: () => void = () => {
- for (const plugin of this._resizePlugins) {
- plugin.resize?.();
- }
- };
-
- private readonly _getPluginParticleColors: (particle: Particle) => (IHsl | undefined)[] = particle => {
- let fColor: IHsl | undefined, sColor: IHsl | undefined;
-
- for (const plugin of this._colorPlugins) {
- if (!fColor && plugin.particleFillColor) {
- fColor = rangeColorToHsl(this._engine, plugin.particleFillColor(particle));
- }
-
- if (!sColor && plugin.particleStrokeColor) {
- sColor = rangeColorToHsl(this._engine, plugin.particleStrokeColor(particle));
- }
-
- if (fColor && sColor) {
- break;
- }
- }
-
- this._reusablePluginColors[fColorIndex] = fColor;
- this._reusablePluginColors[sColorIndex] = sColor;
-
- return this._reusablePluginColors;
- };
-
- private readonly _initStyle: () => void = () => {
- const element = this.element,
- options = this.container.actualOptions;
-
- if (!element) {
- return;
- }
-
- if (this._fullScreen) {
- this._setFullScreenStyle();
- } else {
- this._resetOriginalStyle();
- }
-
- for (const key in options.style) {
- if (!key || !(key in options.style)) {
- continue;
- }
-
- const value = options.style[key];
-
- if (!value) {
- continue;
- }
-
- element.style.setProperty(key, value, "important");
- }
- };
-
- private readonly _repairStyle: () => void = () => {
- const element = this.element;
-
- if (!element) {
- return;
- }
-
- this._safeMutationObserver(observer => {
- observer.disconnect();
- });
- this._initStyle();
- this.initBackground();
-
- const pointerEvents = this._pointerEvents;
-
- element.style.pointerEvents = pointerEvents;
- element.setAttribute("pointer-events", pointerEvents);
-
- this._safeMutationObserver(observer => {
- if (!(element instanceof Node)) {
- return;
- }
-
- observer.observe(element, { attributes: true });
- });
- };
-
- private readonly _resetOriginalStyle: () => void = () => {
- const element = this.element,
- originalStyle = this._originalStyle;
-
- if (!element || !originalStyle) {
- return;
- }
-
- setStyle(element, originalStyle, true);
- };
-
- private readonly _safeMutationObserver: (callback: (observer: MutationObserver) => void) => void = callback => {
- if (!this._mutationObserver) {
- return;
- }
-
- callback(this._mutationObserver);
- };
-
- private readonly _setFullScreenStyle: () => void = () => {
- const element = this.element;
-
- if (!element) {
- return;
- }
-
- setStyle(element, getFullScreenStyle(this.container.actualOptions.fullScreen.zIndex), true);
- };
-}
diff --git a/engine/src/Core/CanvasManager.ts b/engine/src/Core/CanvasManager.ts
new file mode 100644
index 00000000000..f63f7223134
--- /dev/null
+++ b/engine/src/Core/CanvasManager.ts
@@ -0,0 +1,475 @@
+import { cloneStyle, getFullScreenStyle, safeMatchMedia, safeMutationObserver } from "../Utils/Utils.js";
+import { defaultZoom, generatedAttribute, half } from "./Utils/Constants.js";
+import { getStyleFromRgb, rangeColorToRgb } from "../Utils/ColorUtils.js";
+import type { Container } from "./Container.js";
+import type { IContainerPlugin } from "./Interfaces/IContainerPlugin.js";
+import type { ICoordinates } from "./Interfaces/ICoordinates.js";
+import type { IDimension } from "./Interfaces/IDimension.js";
+import type { PluginManager } from "./Utils/PluginManager.js";
+import { RenderManager } from "./RenderManager.js";
+
+/**
+ *
+ * @param canvas -
+ * @param style -
+ * @param important -
+ */
+function setStyle(canvas: HTMLCanvasElement, style?: CSSStyleDeclaration, important = false): void {
+ if (!style) {
+ return;
+ }
+
+ const element = canvas,
+ elementStyle = element.style,
+ keys = new Set();
+
+ for (let i = 0; i < elementStyle.length; i++) {
+ const key = elementStyle.item(i);
+
+ if (!key) {
+ continue;
+ }
+
+ keys.add(key);
+ }
+
+ for (let i = 0; i < style.length; i++) {
+ const key = style.item(i);
+
+ if (!key) {
+ continue;
+ }
+
+ keys.add(key);
+ }
+
+ for (const key of keys) {
+ const value = style.getPropertyValue(key);
+
+ if (value) {
+ elementStyle.setProperty(key, value, important ? "important" : "");
+ } else {
+ elementStyle.removeProperty(key);
+ }
+ }
+}
+
+/**
+ * Canvas manager
+ */
+export class CanvasManager {
+ /**
+ * The particles canvas
+ */
+ element?: HTMLCanvasElement;
+
+ readonly render;
+
+ /**
+ * The particles canvas dimension
+ */
+ readonly size: IDimension;
+
+ /**
+ * Current zoom level
+ */
+ zoom = defaultZoom;
+
+ private readonly _container;
+ private _generated;
+ private _mutationObserver?: MutationObserver;
+ private _originalStyle?: CSSStyleDeclaration;
+ private readonly _pluginManager;
+ private _pointerEvents: string;
+ private _resizePlugins: IContainerPlugin[];
+ private readonly _standardSize: IDimension;
+
+ /**
+ * Zoom center point (for centered zooming)
+ */
+ private _zoomCenter?: ICoordinates;
+
+ /**
+ * Constructor of canvas manager
+ * @param pluginManager - the engine managing the whole library
+ * @param container - the parent container
+ */
+ constructor(pluginManager: PluginManager, container: Container) {
+ this._pluginManager = pluginManager;
+ this._container = container;
+ this.render = new RenderManager(pluginManager, container, this);
+ this._standardSize = {
+ height: 0,
+ width: 0,
+ };
+
+ const pxRatio = container.retina.pixelRatio,
+ stdSize = this._standardSize;
+
+ this.size = {
+ height: stdSize.height * pxRatio,
+ width: stdSize.width * pxRatio,
+ };
+
+ this._generated = false;
+ this._resizePlugins = [];
+ this._pointerEvents = "none";
+ }
+
+ private get _fullScreen(): boolean {
+ return this._container.actualOptions.fullScreen.enable;
+ }
+
+ /**
+ * Destroying object actions
+ */
+ destroy(): void {
+ this.stop();
+
+ if (this._generated) {
+ const element = this.element;
+
+ element?.remove();
+
+ this.element = undefined;
+ } else {
+ this._resetOriginalStyle();
+ }
+
+ this.render.destroy();
+
+ this._resizePlugins = [];
+ }
+
+ getZoomCenter(): ICoordinates {
+ const pxRatio = this._container.retina.pixelRatio,
+ { width, height } = this.size;
+
+ if (this._zoomCenter) {
+ return this._zoomCenter;
+ }
+
+ return {
+ x: (width * half) / pxRatio,
+ y: (height * half) / pxRatio,
+ };
+ }
+
+ /**
+ * Initializes the canvas element
+ */
+ init(): void {
+ this._safeMutationObserver(obs => {
+ obs.disconnect();
+ });
+ this._mutationObserver = safeMutationObserver(records => {
+ for (const record of records) {
+ if (record.type === "attributes" && record.attributeName === "style") {
+ this._repairStyle();
+ }
+ }
+ });
+
+ this.resize();
+ this._initStyle();
+ this.initBackground();
+ this._safeMutationObserver(obs => {
+ if (!this.element || !(this.element instanceof Node)) {
+ return;
+ }
+
+ obs.observe(this.element, { attributes: true });
+ });
+
+ this.initPlugins();
+ this.render.init();
+ }
+
+ /**
+ * Initializes the canvas background
+ */
+ initBackground(): void {
+ const { _container } = this,
+ options = _container.actualOptions,
+ background = options.background,
+ element = this.element;
+
+ if (!element) {
+ return;
+ }
+
+ const elementStyle = element.style,
+ color = rangeColorToRgb(this._pluginManager, background.color);
+
+ if (color) {
+ elementStyle.backgroundColor = getStyleFromRgb(color, _container.hdr, background.opacity);
+ } else {
+ elementStyle.backgroundColor = "";
+ }
+
+ elementStyle.backgroundImage = background.image || "";
+ elementStyle.backgroundPosition = background.position || "";
+ elementStyle.backgroundRepeat = background.repeat || "";
+ elementStyle.backgroundSize = background.size || "";
+ }
+
+ /**
+ * Initializes the plugins needed by canvas
+ */
+ initPlugins(): void {
+ this._resizePlugins = [];
+
+ for (const plugin of this._container.plugins) {
+ if (plugin.resize) {
+ this._resizePlugins.push(plugin);
+ }
+ }
+ }
+
+ /**
+ * Loads the canvas HTML element
+ * @param canvas - the canvas HTML element
+ */
+ loadCanvas(canvas: HTMLCanvasElement): void {
+ if (this._generated && this.element) {
+ this.element.remove();
+ }
+
+ const container = this._container;
+
+ this._generated =
+ generatedAttribute in canvas.dataset ? canvas.dataset[generatedAttribute] === "true" : this._generated;
+ this.element = canvas;
+ this.element.ariaHidden = "true";
+ this._originalStyle = cloneStyle(this.element.style);
+
+ const standardSize = this._standardSize;
+
+ standardSize.height = canvas.offsetHeight;
+ standardSize.width = canvas.offsetWidth;
+
+ const pxRatio = this._container.retina.pixelRatio,
+ retinaSize = this.size;
+
+ canvas.height = retinaSize.height = standardSize.height * pxRatio;
+ canvas.width = retinaSize.width = standardSize.width * pxRatio;
+
+ const canSupportHdrQuery = safeMatchMedia("(color-gamut: p3)");
+
+ this.render.setContextSettings({
+ alpha: true,
+ colorSpace: canSupportHdrQuery?.matches && container.hdr ? "display-p3" : "srgb",
+ desynchronized: true,
+ willReadFrequently: false,
+ });
+ this.render.setContext(this.element.getContext("2d", this.render.settings));
+
+ this._safeMutationObserver(obs => {
+ obs.disconnect();
+ });
+
+ container.retina.init();
+ this.initBackground();
+
+ this._safeMutationObserver(obs => {
+ if (!this.element || !(this.element instanceof Node)) {
+ return;
+ }
+
+ obs.observe(this.element, { attributes: true });
+ });
+ }
+
+ /**
+ * Calculates the size of the canvas
+ * @returns true if the size changed
+ */
+ resize(): boolean {
+ if (!this.element) {
+ return false;
+ }
+
+ const container = this._container,
+ currentSize = container.canvas._standardSize,
+ newSize = {
+ width: this.element.offsetWidth,
+ height: this.element.offsetHeight,
+ },
+ pxRatio = container.retina.pixelRatio,
+ retinaSize = {
+ width: newSize.width * pxRatio,
+ height: newSize.height * pxRatio,
+ };
+
+ if (
+ newSize.height === currentSize.height &&
+ newSize.width === currentSize.width &&
+ retinaSize.height === this.element.height &&
+ retinaSize.width === this.element.width
+ ) {
+ return false;
+ }
+
+ const oldSize = { ...currentSize };
+
+ currentSize.height = newSize.height;
+ currentSize.width = newSize.width;
+
+ const canvasSize = this.size;
+
+ this.element.width = canvasSize.width = retinaSize.width;
+ this.element.height = canvasSize.height = retinaSize.height;
+
+ if (this._container.started) {
+ container.particles.setResizeFactor({
+ width: currentSize.width / oldSize.width,
+ height: currentSize.height / oldSize.height,
+ });
+ }
+
+ return true;
+ }
+
+ setPointerEvents(type: string): void {
+ const element = this.element;
+
+ if (!element) {
+ return;
+ }
+
+ this._pointerEvents = type;
+ this._repairStyle();
+ }
+
+ /**
+ * Sets the zoom level and center point
+ * @param zoomLevel - the new zoom level
+ * @param center - optional center point for zoom (default is canvas center)
+ */
+ setZoom(zoomLevel: number, center?: ICoordinates): void {
+ this.zoom = zoomLevel;
+ this._zoomCenter = center;
+ }
+
+ stop(): void {
+ this._safeMutationObserver(obs => {
+ obs.disconnect();
+ });
+
+ this._mutationObserver = undefined;
+
+ this.render.stop();
+ }
+
+ /**
+ * The window resize event handler
+ */
+ async windowResize(): Promise {
+ if (!this.element || !this.resize()) {
+ return;
+ }
+
+ const container = this._container,
+ needsRefresh = container.updateActualOptions();
+
+ /* density particles enabled */
+ container.particles.setDensity();
+
+ this._applyResizePlugins();
+
+ if (needsRefresh) {
+ await container.refresh();
+ }
+ }
+
+ private readonly _applyResizePlugins: () => void = () => {
+ for (const plugin of this._resizePlugins) {
+ plugin.resize?.();
+ }
+ };
+
+ private readonly _initStyle: () => void = () => {
+ const element = this.element,
+ options = this._container.actualOptions;
+
+ if (!element) {
+ return;
+ }
+
+ if (this._fullScreen) {
+ this._setFullScreenStyle();
+ } else {
+ this._resetOriginalStyle();
+ }
+
+ for (const key in options.style) {
+ if (!key || !(key in options.style)) {
+ continue;
+ }
+
+ const value = options.style[key];
+
+ if (!value) {
+ continue;
+ }
+
+ element.style.setProperty(key, value, "important");
+ }
+ };
+
+ private readonly _repairStyle: () => void = () => {
+ const element = this.element;
+
+ if (!element) {
+ return;
+ }
+
+ this._safeMutationObserver(observer => {
+ observer.disconnect();
+ });
+ this._initStyle();
+ this.initBackground();
+
+ const pointerEvents = this._pointerEvents;
+
+ element.style.pointerEvents = pointerEvents;
+ element.setAttribute("pointer-events", pointerEvents);
+
+ this._safeMutationObserver(observer => {
+ if (!(element instanceof Node)) {
+ return;
+ }
+
+ observer.observe(element, { attributes: true });
+ });
+ };
+
+ private readonly _resetOriginalStyle: () => void = () => {
+ const element = this.element,
+ originalStyle = this._originalStyle;
+
+ if (!element || !originalStyle) {
+ return;
+ }
+
+ setStyle(element, originalStyle, true);
+ };
+
+ private readonly _safeMutationObserver: (callback: (observer: MutationObserver) => void) => void = callback => {
+ if (!this._mutationObserver) {
+ return;
+ }
+
+ callback(this._mutationObserver);
+ };
+
+ private readonly _setFullScreenStyle: () => void = () => {
+ const element = this.element;
+
+ if (!element) {
+ return;
+ }
+
+ setStyle(element, getFullScreenStyle(this._container.actualOptions.fullScreen.zIndex), true);
+ };
+}
diff --git a/engine/src/Core/Container.ts b/engine/src/Core/Container.ts
index 4a16019486a..c394fcfff93 100644
--- a/engine/src/Core/Container.ts
+++ b/engine/src/Core/Container.ts
@@ -1,26 +1,31 @@
import { animate, cancelAnimation, getRangeValue } from "../Utils/MathUtils.js";
-import {
- defaultFps,
- defaultFpsLimit,
- millisecondsToSeconds,
- minFpsLimit,
- removeDeleteCount,
- removeMinIndex,
-} from "./Utils/Constants.js";
-import { Canvas } from "./Canvas.js";
-import type { Engine } from "./Engine.js";
+import { defaultFps, defaultFpsLimit, millisecondsToSeconds, minFpsLimit } from "./Utils/Constants.js";
+import { CanvasManager } from "./CanvasManager.js";
+import type { CustomEventArgs } from "../Types/CustomEventArgs.js";
import { EventListeners } from "./Utils/EventListeners.js";
import { EventType } from "../Enums/Types/EventType.js";
import type { IContainerPlugin } from "./Interfaces/IContainerPlugin.js";
import type { IDelta } from "./Interfaces/IDelta.js";
+import { type IEffectDrawer } from "./Interfaces/IEffectDrawer.js";
+import { type IParticleUpdater } from "./Interfaces/IParticleUpdater.js";
import type { IPlugin } from "./Interfaces/IPlugin.js";
+import { type IShapeDrawer } from "./Interfaces/IShapeDrawer.js";
import type { ISourceOptions } from "../Types/ISourceOptions.js";
import { Options } from "../Options/Classes/Options.js";
-import { Particles } from "./Particles.js";
+import { ParticlesManager } from "./ParticlesManager.js";
+import type { PluginManager } from "./Utils/PluginManager.js";
import { Retina } from "./Retina.js";
import { getLogger } from "../Utils/LogUtils.js";
import { loadOptions } from "../Utils/OptionsUtils.js";
+interface ContainerParams {
+ dispatchCallback: (eventType: string, args?: CustomEventArgs) => void;
+ id: string;
+ onDestroy: (remove: boolean) => void;
+ pluginManager: PluginManager;
+ sourceOptions?: ISourceOptions;
+}
+
/**
* Checks if the container is still usable
* @param container - the container to check
@@ -42,17 +47,17 @@ function updateDelta(delta: IDelta, value: number, fpsLimit = defaultFps, smooth
}
/**
- * @param engine -
+ * @param pluginManager -
* @param container -
* @param sourceOptionsArr -
* @returns the options loaded
*/
function loadContainerOptions(
- engine: Engine,
+ pluginManager: PluginManager,
container: Container,
...sourceOptionsArr: (ISourceOptions | undefined)[]
): Options {
- const options = new Options(engine, container);
+ const options = new Options(pluginManager, container);
loadOptions(options, ...sourceOptionsArr);
@@ -79,6 +84,8 @@ export class Container {
*/
destroyed;
+ effectDrawers: Map;
+
/**
* The container fps limit, coming from options
*/
@@ -100,6 +107,8 @@ export class Container {
readonly particleDestroyedPlugins: IContainerPlugin[];
readonly particlePositionPlugins: IContainerPlugin[];
+ particleUpdaters: IParticleUpdater[];
+
/**
* The particles manager
*/
@@ -112,6 +121,8 @@ export class Container {
readonly retina;
+ shapeDrawers: Map;
+
/**
* Check if the particles container is started
*/
@@ -122,12 +133,12 @@ export class Container {
private _delay: number;
private _delayTimeout?: number | NodeJS.Timeout;
private readonly _delta: IDelta = { value: 0, factor: 0 };
+ private readonly _dispatchCallback;
private _drawAnimationFrame?: number;
/**
* The container duration
*/
private _duration;
- private readonly _engine;
private readonly _eventListeners;
private _firstStart;
private _initialSourceOptions;
@@ -139,19 +150,23 @@ export class Container {
* The container lifetime
*/
private _lifeTime;
+ private readonly _onDestroy;
private _options;
private _paused;
+ private readonly _pluginManager;
private _smooth;
private _sourceOptions;
/**
* This is the core class, create an instance to have a new working particles manager
- * @param engine - the engine used by container
- * @param id - the id to identify this instance
- * @param sourceOptions - the options to load
+ * @param params -
*/
- constructor(engine: Engine, id: string, sourceOptions?: ISourceOptions) {
- this._engine = engine;
+ constructor(params: ContainerParams) {
+ const { dispatchCallback, pluginManager, id, onDestroy, sourceOptions } = params;
+
+ this._pluginManager = pluginManager;
+ this._dispatchCallback = dispatchCallback;
+ this._onDestroy = onDestroy;
this.id = Symbol(id);
this.fpsLimit = 120;
this.hdr = false;
@@ -168,20 +183,23 @@ export class Container {
this.pageHidden = false;
this._sourceOptions = sourceOptions;
this._initialSourceOptions = sourceOptions;
+ this.effectDrawers = new Map();
+ this.shapeDrawers = new Map();
+ this.particleUpdaters = [];
this.retina = new Retina(this);
- this.canvas = new Canvas(this, this._engine);
- this.particles = new Particles(this._engine, this);
+ this.canvas = new CanvasManager(this._pluginManager, this);
+ this.particles = new ParticlesManager(this._pluginManager, this);
this.plugins = [];
this.particleDestroyedPlugins = [];
this.particleCreatedPlugins = [];
this.particlePositionPlugins = [];
/* tsParticles variables with default values */
- this._options = loadContainerOptions(this._engine, this);
- this.actualOptions = loadContainerOptions(this._engine, this);
+ this._options = loadContainerOptions(this._pluginManager, this);
+ this.actualOptions = loadContainerOptions(this._pluginManager, this);
/* ---------- tsParticles - start ------------ */
this._eventListeners = new EventListeners(this);
- this._engine.dispatchEvent(EventType.containerBuilt, { container: this });
+ this.dispatchEvent(EventType.containerBuilt);
}
/**
@@ -230,26 +248,37 @@ export class Container {
this.particles.destroy();
this.canvas.destroy();
+ for (const [, effectDrawer] of this.effectDrawers) {
+ effectDrawer.destroy?.(this);
+ }
+
+ for (const [, shapeDrawer] of this.shapeDrawers) {
+ shapeDrawer.destroy?.(this);
+ }
+
for (const plugin of this.plugins) {
plugin.destroy?.();
}
+ this.effectDrawers = new Map();
+ this.shapeDrawers = new Map();
+ this.particleUpdaters = [];
this.plugins.length = 0;
- this._engine.clearPlugins(this);
+ this._pluginManager.clearPlugins(this);
this.destroyed = true;
- if (remove) {
- const mainArr = this._engine.items,
- idx = mainArr.indexOf(this);
+ this._onDestroy(remove);
- if (idx >= removeMinIndex) {
- mainArr.splice(idx, removeDeleteCount);
- }
- }
+ this.dispatchEvent(EventType.containerDestroyed);
+ }
- this._engine.dispatchEvent(EventType.containerDestroyed, { container: this });
+ dispatchEvent(type: string, data?: unknown): void {
+ this._dispatchCallback(type, {
+ container: this,
+ data,
+ });
}
/**
@@ -304,7 +333,7 @@ export class Container {
const allContainerPlugins = new Map();
- for (const plugin of this._engine.plugins) {
+ for (const plugin of this._pluginManager.plugins) {
const containerPlugin = await plugin.getPlugin(this);
if (containerPlugin.preInit) {
@@ -314,11 +343,11 @@ export class Container {
allContainerPlugins.set(plugin, containerPlugin);
}
- await this.particles.initPlugins();
+ await this.initDrawersAndUpdaters();
/* options settings */
- this._options = loadContainerOptions(this._engine, this, this._initialSourceOptions, this.sourceOptions);
- this.actualOptions = loadContainerOptions(this._engine, this, this._options);
+ this._options = loadContainerOptions(this._pluginManager, this, this._initialSourceOptions, this.sourceOptions);
+ this.actualOptions = loadContainerOptions(this._pluginManager, this, this._options);
this.plugins.length = 0;
this.particleDestroyedPlugins.length = 0;
@@ -368,7 +397,7 @@ export class Container {
await this.particles.init();
- this._engine.dispatchEvent(EventType.containerInit, { container: this });
+ this.dispatchEvent(EventType.containerInit);
this.particles.setDensity();
@@ -376,7 +405,15 @@ export class Container {
plugin.particlesSetup?.();
}
- this._engine.dispatchEvent(EventType.particlesSetup, { container: this });
+ this.dispatchEvent(EventType.particlesSetup);
+ }
+
+ async initDrawersAndUpdaters(): Promise {
+ const pluginManager = this._pluginManager;
+
+ this.effectDrawers = await pluginManager.getEffectDrawers(this, true);
+ this.shapeDrawers = await pluginManager.getShapeDrawers(this, true);
+ this.particleUpdaters = await pluginManager.getUpdaters(this, true);
}
/**
@@ -405,7 +442,7 @@ export class Container {
this._paused = true;
}
- this._engine.dispatchEvent(EventType.containerPaused, { container: this });
+ this.dispatchEvent(EventType.containerPaused);
}
/**
@@ -437,7 +474,7 @@ export class Container {
}
}
- this._engine.dispatchEvent(EventType.containerPlay, { container: this });
+ this.dispatchEvent(EventType.containerPlay);
this.draw(needsUpdate ?? false);
}
@@ -464,8 +501,8 @@ export class Container {
this._initialSourceOptions = sourceOptions;
this._sourceOptions = sourceOptions;
- this._options = loadContainerOptions(this._engine, this, this._initialSourceOptions, this.sourceOptions);
- this.actualOptions = loadContainerOptions(this._engine, this, this._options);
+ this._options = loadContainerOptions(this._pluginManager, this, this._initialSourceOptions, this.sourceOptions);
+ this.actualOptions = loadContainerOptions(this._pluginManager, this, this._options);
return this.refresh();
}
@@ -490,7 +527,7 @@ export class Container {
await plugin.start?.();
}
- this._engine.dispatchEvent(EventType.containerStarted, { container: this });
+ this.dispatchEvent(EventType.containerStarted);
this.play();
@@ -532,7 +569,7 @@ export class Container {
this._sourceOptions = this._options;
- this._engine.dispatchEvent(EventType.containerStopped, { container: this });
+ this.dispatchEvent(EventType.containerStopped);
}
/**
@@ -576,7 +613,7 @@ export class Container {
return;
}
- this.canvas.drawParticles(this._delta);
+ this.canvas.render.drawParticles(this._delta);
if (!this.alive()) {
this.destroy();
diff --git a/engine/src/Core/Engine.ts b/engine/src/Core/Engine.ts
index 54947f57662..d9107c53d84 100644
--- a/engine/src/Core/Engine.ts
+++ b/engine/src/Core/Engine.ts
@@ -2,13 +2,6 @@
* Engine class for creating the singleton on globalThis.
* It's a singleton class for initializing {@link Container} instances
*/
-import type { EasingType, EasingTypeAlt } from "../Enums/Types/EasingType.js";
-import type {
- EffectInitializer,
- Initializers,
- ShapeInitializer,
- UpdaterInitializer,
-} from "../Types/EngineInitializers.js";
import {
canvasFirstIndex,
canvasTag,
@@ -20,30 +13,16 @@ import {
none,
one,
removeDeleteCount,
+ removeMinIndex,
} from "./Utils/Constants.js";
-import {
- getItemMapFromInitializer,
- getItemsFromInitializer,
- itemFromSingleOrMultiple,
- safeDocument,
-} from "../Utils/Utils.js";
+import { itemFromSingleOrMultiple, safeDocument } from "../Utils/Utils.js";
import type { Container } from "./Container.js";
import type { CustomEventArgs } from "../Types/CustomEventArgs.js";
import type { CustomEventListener } from "../Types/CustomEventListener.js";
-import type { EasingFunction } from "../Types/EasingFunction.js";
import { EventDispatcher } from "../Utils/EventDispatcher.js";
-import { EventType } from "../Enums/Types/EventType.js";
-import type { IColorManager } from "./Interfaces/IColorManager.js";
-import type { IEffectDrawer } from "./Interfaces/IEffectDrawer.js";
import type { ILoadParams } from "./Interfaces/ILoadParams.js";
-import type { IPalette } from "./Interfaces/IPalette.js";
-import type { IParticleUpdater } from "./Interfaces/IParticleUpdater.js";
-import type { IParticlesOptions } from "../Options/Interfaces/Particles/IParticlesOptions.js";
-import type { IPlugin } from "./Interfaces/IPlugin.js";
-import type { IShapeDrawer } from "./Interfaces/IShapeDrawer.js";
import type { ISourceOptions } from "../Types/ISourceOptions.js";
-import type { ParticlesOptions } from "../Options/Classes/Particles/ParticlesOptions.js";
-import type { RecursivePartial } from "../Types/RecursivePartial.js";
+import { PluginManager } from "./Utils/PluginManager.js";
import type { SingleOrMultiple } from "../Types/SingleOrMultiple.js";
import { getLogger } from "../Utils/LogUtils.js";
import { getRandom } from "../Utils/MathUtils.js";
@@ -62,16 +41,6 @@ interface DataFromUrlParams {
url: SingleOrMultiple;
}
-type AsyncLoadPluginFunction = (engine: Engine) => Promise;
-type SyncLoadPluginFunction = (engine: Engine) => void;
-type AsyncLoadPluginNoEngine = () => Promise;
-type SyncLoadPluginNoEngine = () => void;
-type LoadPluginFunction =
- | AsyncLoadPluginFunction
- | SyncLoadPluginFunction
- | AsyncLoadPluginNoEngine
- | SyncLoadPluginNoEngine;
-
/**
* @param data -
* @returns the options object from the jsonUrl
@@ -88,7 +57,7 @@ async function getDataFromUrl(
const response = await fetch(url);
if (response.ok) {
- return (await response.json()) as SingleOrMultiple;
+ return (await response.json()) as SingleOrMultiple>;
}
getLogger().error(`${response.status.toString()} while retrieving config file`);
@@ -139,7 +108,7 @@ const getCanvasFromContainer = (domContainer: HTMLElement): HTMLCanvasElement =>
return domContainer;
}
- domContainer = documentSafe.createElement("div");
+ domContainer = documentSafe.createElement("canvas");
domContainer.id = id;
domContainer.dataset[generatedAttribute] = generatedTrue;
@@ -155,47 +124,7 @@ const getCanvasFromContainer = (domContainer: HTMLElement): HTMLCanvasElement =>
* and for Plugins class responsible for every external feature
*/
export class Engine {
- readonly colorManagers = new Map();
-
- readonly easingFunctions = new Map();
-
- /**
- * The drawers (additional effects) array
- */
- readonly effectDrawers = new Map>();
-
- readonly initializers: Initializers = {
- effects: new Map(),
- shapes: new Map(),
- updaters: new Map(),
- };
-
- readonly palettes = new Map();
-
- /**
- * The plugins array
- */
- readonly plugins: IPlugin[] = [];
-
- /**
- * The presets array
- */
- readonly presets = new Map();
-
- /**
- * The drawers (additional shapes) array
- */
- readonly shapeDrawers = new Map>();
-
- /**
- * The updaters array
- */
- readonly updaters = new Map();
-
- private _allLoadersSet = new Set();
-
- private readonly _configs = new Map();
-
+ readonly pluginManager = new PluginManager(this);
/**
* Contains all the {@link Container} instances of the current engine instance
*/
@@ -203,27 +132,11 @@ export class Engine {
private readonly _eventDispatcher = new EventDispatcher();
- private _executedSet = new Set();
-
/**
* Checks if the engine instance is initialized
*/
private _initialized = false;
- private _isRunningLoaders = false;
-
- private readonly _loadPromises = new Set();
-
- get configs(): Record {
- const res: Record = {};
-
- for (const [name, config] of this._configs) {
- res[name] = config;
- }
-
- return res;
- }
-
get items(): Container[] {
return this._domArray;
}
@@ -232,42 +145,6 @@ export class Engine {
return __VERSION__;
}
- /**
- * @param name -
- * @param manager -
- */
- addColorManager(name: string, manager: IColorManager): void {
- this.colorManagers.set(name, manager);
- }
-
- addConfig(config: ISourceOptions): void {
- const key = config.key ?? config.name ?? "default";
-
- this._configs.set(key, config);
- this._eventDispatcher.dispatchEvent(EventType.configAdded, { data: { name: key, config } });
- }
-
- /**
- * @param name -
- * @param easing -
- */
- addEasing(name: EasingType | EasingTypeAlt, easing: EasingFunction): void {
- if (this.easingFunctions.get(name)) {
- return;
- }
-
- this.easingFunctions.set(name, easing);
- }
-
- /**
- * addEffect adds effect to tsParticles, it will be available to all future instances created
- * @param effect - the effect name
- * @param drawer - the effect drawer function or class instance that draws the effect in the canvas
- */
- addEffect(effect: string, drawer: EffectInitializer): void {
- this.initializers.effects.set(effect, drawer);
- }
-
/**
* Adds a listener to the specified event
* @param type - The event to listen to
@@ -277,56 +154,6 @@ export class Engine {
this._eventDispatcher.addEventListener(type, listener);
}
- addPalette(name: string, palette: IPalette): void {
- this.palettes.set(name, palette);
- }
-
- /**
- * Adds a particle updater to the collection
- * @param name - the particle updater name used as a key
- * @param updaterInitializer - the particle updater initializer
- */
- addParticleUpdater(name: string, updaterInitializer: UpdaterInitializer): void {
- this.initializers.updaters.set(name, updaterInitializer);
- }
-
- /**
- * addPlugin adds plugin to tsParticles, if an instance needs it, it will be loaded
- * @param plugin - the plugin implementation of {@link IPlugin}
- */
- addPlugin(plugin: IPlugin): void {
- if (this.getPlugin(plugin.id)) {
- return;
- }
-
- this.plugins.push(plugin);
- }
-
- /**
- * addPreset adds preset to tsParticles, it will be available to all future instances created
- * @param preset - the preset name
- * @param options - the options to add to the preset
- * @param override - if true, the preset will override any existing with the same name
- */
- addPreset(preset: string, options: Readonly, override = false): void {
- if (!(override || !this.getPreset(preset))) {
- return;
- }
-
- this.presets.set(preset, options);
- }
-
- /**
- * addShape adds shape to tsParticles, it will be available to all future instances created
- * @param shapes - the shape names to add, it can be a single shape or an array of shapes
- * @param drawer - the shape drawer function or class instance that draws the shape in the canvas
- */
- addShape(shapes: string[], drawer: ShapeInitializer): void {
- for (const shape of shapes) {
- this.initializers.shapes.set(shape, drawer);
- }
- }
-
/**
* @param pluginVersion - the plugin version to check against
*/
@@ -340,12 +167,6 @@ export class Engine {
);
}
- clearPlugins(container: Container): void {
- this.effectDrawers.delete(container);
- this.shapeDrawers.delete(container);
- this.updaters.delete(container);
- }
-
/**
* Dispatches an event that will be listened from listeners
* @param type - The event to dispatch
@@ -355,76 +176,17 @@ export class Engine {
this._eventDispatcher.dispatchEvent(type, args);
}
- /**
- * @param name -
- * @returns the easing function
- */
- getEasing(name: EasingType | EasingTypeAlt): EasingFunction {
- return this.easingFunctions.get(name) ?? ((value: number): number => value);
- }
-
- getEffectDrawers(container: Container, force = false): Promise