position: fixed; top: 10px; left: 10px;
color: white; font-family: Arial; pointer-events: none;
}
+
+ #resetBtn {
+ position: fixed;
+ top: 20px;
+ left: 20px;
+ padding: 12px 24px;
+ background: rgba(255, 255, 255, 0.1);
+ border: 2px solid rgba(255, 255, 255, 0.3);
+ color: white;
+ font-family: Arial;
+ font-size: 16px;
+ border-radius: 30px;
+ cursor: pointer;
+ backdrop-filter: blur(5px);
+ transition: all 0.3s ease;
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
+ z-index: 1000;
+ }
+
+ #resetBtn:hover {
+ background: rgba(255, 0, 100, 0.3);
+ border-color: rgba(255, 0, 100, 0.7);
+ transform: scale(1.05);
+ }
+
+ #resetBtn:active {
+ transform: scale(0.95);
+ background: rgba(255, 0, 100, 0.3);
+ border-color: rgba(255, 255, 255, 0.3);
+ }
+
+ @media (hover: none) {
+ #resetBtn:hover {
+ background: rgba(255, 255, 255, 0.1);
+ border-color: rgba(255, 255, 255, 0.3);
+ transform: none;
+ }
+ }
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<div id="status"></div>
+ <button id="resetBtn">Reset</button>
<script src="matter.min.js"></script>
<script>
- // Configuration variables with detailed comments
const CONFIG = {
- // Particle properties:
PARTICLE_RADIUS: 5,
PARTICLE_VARIATION: 4,
PARTICLE_RESTITUTION: 0.6,
PARTICLE_FRICTION: 0.00,
PARTICLE_FRICTION_AIR: 0.000,
GRAVITY_SCALE: 0.005,
-
- // Shake behavior:
SHAKE_FORCE: 0.2,
SHAKE_THRESHOLD: 12,
-
- // Walls:
WALL_THICKNESS: 40,
WALL_RESTITUTION: 0.8,
-
- // Spawn behavior:
SPAWN_FORCE: 0,
PARTICLES_PER_SPAWN: 3,
-
- // World gravity:
WORLD_GRAVITY: 1.0
};
const canvas = document.getElementById('canvas');
const statusDiv = document.getElementById('status');
+ const resetBtn = document.getElementById('resetBtn');
let engine, runner, render;
let walls = [];
let isInitialized = false;
- // Track continuous input for spawning particles.
- let isPointerDown = false;
- let pointerX = null;
- let pointerY = null;
+ // Track multiple touches and mouse input
+ let activeTouches = new Map(); // Stores active touch points
+ let isMouseDown = false; // Tracks mouse state
- // Helper function to generate a random neon color.
function randomNeonColor() {
return `hsl(${Math.floor(Math.random() * 360)}, 100%, 50%)`;
}
resize();
createWalls();
- // Set up pointer events for continuous particle spawning.
- canvas.addEventListener('mousedown', onPointerDown, false);
- canvas.addEventListener('mousemove', onPointerMove, false);
- canvas.addEventListener('mouseup', onPointerUp, false);
+ // Mouse events
+ canvas.addEventListener('mousedown', onMouseDown, false);
+ canvas.addEventListener('mousemove', onMouseMove, false);
+ canvas.addEventListener('mouseup', onMouseUp, false);
+ // Touch events
canvas.addEventListener('touchstart', onTouchStart, { passive: false });
canvas.addEventListener('touchmove', onTouchMove, { passive: false });
canvas.addEventListener('touchend', onTouchEnd, { passive: false });
spawnLoop();
}
- // Schedule the simulated touch after the full window load event.
- window.addEventListener('load', () => {
- setTimeout(simulateTouch, 25);
- });
-
- function simulateTouch() {
- const simulatedX = canvas.width / 2;
- const simulatedY = canvas.height * 0.25;
- let touchObj;
- try {
- touchObj = new Touch({
- identifier: Date.now(),
- target: canvas,
- clientX: simulatedX,
- clientY: simulatedY,
- pageX: simulatedX,
- pageY: simulatedY,
- screenX: simulatedX,
- screenY: simulatedY
- });
- } catch (e) {
- touchObj = { clientX: simulatedX, clientY: simulatedY };
- }
+ // Mouse event handlers
+ function onMouseDown(event) {
+ isMouseDown = true;
+ activeTouches.set('mouse', {
+ x: event.clientX,
+ y: event.clientY
+ });
+ }
- let touchStartEvent;
- try {
- touchStartEvent = new TouchEvent('touchstart', {
- cancelable: true,
- bubbles: true,
- touches: [touchObj],
- targetTouches: [touchObj],
- changedTouches: [touchObj]
+ function onMouseMove(event) {
+ if (isMouseDown) {
+ activeTouches.set('mouse', {
+ x: event.clientX,
+ y: event.clientY
});
- } catch (e) {
- touchStartEvent = new Event('touchstart', { bubbles: true, cancelable: true });
- touchStartEvent.touches = [touchObj];
- touchStartEvent.targetTouches = [touchObj];
- touchStartEvent.changedTouches = [touchObj];
}
- canvas.dispatchEvent(touchStartEvent);
-
- // End the simulated touch after a short delay.
- setTimeout(() => {
- let touchEndEvent;
- try {
- touchEndEvent = new TouchEvent('touchend', {
- cancelable: true,
- bubbles: true,
- touches: [],
- targetTouches: [],
- changedTouches: [touchObj]
- });
- } catch (e) {
- touchEndEvent = new Event('touchend', { bubbles: true, cancelable: true });
- touchEndEvent.touches = [];
- touchEndEvent.targetTouches = [];
- touchEndEvent.changedTouches = [touchObj];
- }
- canvas.dispatchEvent(touchEndEvent);
- }, 100);
}
- function spawnLoop() {
- if (isPointerDown && pointerX !== null && pointerY !== null) {
- for (let i = 0; i < CONFIG.PARTICLES_PER_SPAWN; i++) {
- createParticle(pointerX, pointerY);
- }
- }
- requestAnimationFrame(spawnLoop);
- }
-
- function onPointerDown(event) {
- isPointerDown = true;
- pointerX = event.clientX;
- pointerY = event.clientY;
- }
- function onPointerMove(event) {
- if (isPointerDown) {
- pointerX = event.clientX;
- pointerY = event.clientY;
- }
- }
- function onPointerUp() {
- isPointerDown = false;
- pointerX = null;
- pointerY = null;
+ function onMouseUp() {
+ isMouseDown = false;
+ activeTouches.delete('mouse');
}
+ // Touch event handlers
function onTouchStart(event) {
event.preventDefault();
- isPointerDown = true;
- const touch = event.touches[0];
- pointerX = touch.clientX;
- pointerY = touch.clientY;
+ Array.from(event.changedTouches).forEach(touch => {
+ activeTouches.set(touch.identifier, {
+ x: touch.clientX,
+ y: touch.clientY
+ });
+ });
}
+
function onTouchMove(event) {
event.preventDefault();
- if (isPointerDown && event.touches.length > 0) {
- const touch = event.touches[0];
- pointerX = touch.clientX;
- pointerY = touch.clientY;
- }
+ Array.from(event.changedTouches).forEach(touch => {
+ if (activeTouches.has(touch.identifier)) {
+ activeTouches.set(touch.identifier, {
+ x: touch.clientX,
+ y: touch.clientY
+ });
+ }
+ });
}
+
function onTouchEnd(event) {
event.preventDefault();
- if (event.touches.length === 0) {
- isPointerDown = false;
- pointerX = null;
- pointerY = null;
- }
+ Array.from(event.changedTouches).forEach(touch => {
+ activeTouches.delete(touch.identifier);
+ });
+ }
+
+ // Spawn particles for all active touch points
+ function spawnLoop() {
+ activeTouches.forEach(touch => {
+ for (let i = 0; i < CONFIG.PARTICLES_PER_SPAWN; i++) {
+ createParticle(touch.x, touch.y);
+ }
+ });
+ requestAnimationFrame(spawnLoop);
}
function createParticle(x, y) {
createWalls();
}
- let lastShake = Date.now();
+ function removeAllBalls() {
+ Matter.Composite.allBodies(engine.world).forEach(body => {
+ if (!body.isStatic) {
+ Matter.Composite.remove(engine.world, body);
+ }
+ });
+ }
+ // Handle device motion for gravity effects
function handleMotion(e) {
const acc = e.accelerationIncludingGravity;
if (!acc) return;
}
}
+ // Request permission for device motion sensors
function requestSensorPermissions() {
if (typeof DeviceMotionEvent !== "undefined" && typeof DeviceMotionEvent.requestPermission === "function") {
DeviceMotionEvent.requestPermission().then(state => {
}
}
+ // Reset button event handlers
+ resetBtn.addEventListener('mousedown', () => {
+ resetBtn.style.background = 'rgba(255, 0, 100, 0.3)';
+ resetBtn.style.borderColor = 'rgba(255, 0, 100, 0.7)';
+ });
+ resetBtn.addEventListener('mouseup', () => {
+ resetBtn.style.background = 'rgba(255, 255, 255, 0.1)';
+ resetBtn.style.borderColor = 'rgba(255, 255, 255, 0.3)';
+ });
+ resetBtn.addEventListener('mouseleave', () => {
+ resetBtn.style.background = 'rgba(255, 255, 255, 0.1)';
+ resetBtn.style.borderColor = 'rgba(255, 255, 255, 0.3)';
+ });
+
+ resetBtn.addEventListener('touchstart', () => {
+ resetBtn.style.background = 'rgba(255, 0, 100, 0.3)';
+ resetBtn.style.borderColor = 'rgba(255, 0, 100, 0.7)';
+ removeAllBalls();
+ });
+ resetBtn.addEventListener('touchend', () => {
+ resetBtn.style.background = 'rgba(255, 255, 255, 0.1)';
+ resetBtn.style.borderColor = 'rgba(255, 255, 255, 0.3)';
+ });
+
init();
</script>
</body>
</html>
-