+++ /dev/null
-<!DOCTYPE html>
-<html>
-<head>
- <title>Gravity Balls</title>
- <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
- <style>
- body { margin: 0; overflow: hidden; background: #000; }
- canvas { touch-action: none; }
- #status {
- position: fixed; top: 10px; left: 10px;
- color: white; font-family: Arial; pointer-events: none;
- }
- </style>
-</head>
-<body>
- <canvas id="canvas"></canvas>
- <div id="status"></div>
- <script src="matter.min.js"></script>
- <script>
- // Configuration variables with detailed comments
- const CONFIG = {
- // Particle properties:
- // Base radius of a particle in pixels. The final radius is this value plus a random variation.
- PARTICLE_RADIUS: 5,
- // Maximum additional random value for the particle's radius.
- PARTICLE_VARIATION: 4,
- // Restitution defines the bounciness of particles (0 = no bounce, 1 = perfect bounce).
- PARTICLE_RESTITUTION: 0.6,
- // Friction applied during collisions between particles.
- PARTICLE_FRICTION: 0.00,
- // Additional air friction to help particles slow down over time.
- PARTICLE_FRICTION_AIR: 0.000,
- // Scale factor to convert device acceleration into a gravity vector.
- // This affects how strongly device tilt influences the simulation's gravity.
- GRAVITY_SCALE: 0.005,
-
- // Shake behavior:
- // Additional force applied to particles during a shake event.
- SHAKE_FORCE: 0.2, // Increased for a stronger shake effect when triggered.
- // Threshold acceleration (in m/s²) required to trigger a shake event.
- // Increased to 15 so that normal gravitational acceleration doesn't continuously trigger a shake.
- SHAKE_THRESHOLD: 12,
-
- // Walls:
- // Thickness (in pixels) of the boundary walls around the simulation canvas.
- WALL_THICKNESS: 40,
- // Restitution (bounciness) of the walls when particles collide with them.
- WALL_RESTITUTION: 0.8,
-
- // Spawn behavior:
- // Initial force applied to particles when they are spawned.
- // Set to 0 to prevent particles from being shot out randomly.
- SPAWN_FORCE: 0,
- // Number of particles created per spawn event (e.g., each time the user taps or holds).
- PARTICLES_PER_SPAWN: 3,
-
- // World gravity:
- // This variable controls the default downward gravity of the simulation.
- // Increase this value to make particles fall faster regardless of device tilt.
- WORLD_GRAVITY: 1.0
- };
-
- const canvas = document.getElementById('canvas');
- const statusDiv = document.getElementById('status');
- let engine, runner, render;
- let walls = [];
- let isInitialized = false;
-
- // Track continuous input for spawning particles.
- let isPointerDown = false;
- let pointerX = null;
- let pointerY = null;
-
- // Helper function to generate a random neon color.
- function randomNeonColor() {
- return `hsl(${Math.floor(Math.random() * 360)}, 100%, 50%)`;
- }
-
- function init() {
- if (isInitialized) return;
- isInitialized = true;
-
- engine = Matter.Engine.create();
- // Set the default downward gravity using our config variable.
- engine.world.gravity.y = CONFIG.WORLD_GRAVITY;
-
- runner = Matter.Runner.create();
- Matter.Runner.run(runner, engine);
-
- render = Matter.Render.create({
- canvas: canvas,
- engine: engine,
- options: {
- width: window.innerWidth,
- height: window.innerHeight,
- wireframes: false,
- background: 'transparent'
- }
- });
- Matter.Render.run(render);
-
- 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);
-
- canvas.addEventListener('touchstart', onTouchStart, { passive: false });
- canvas.addEventListener('touchmove', onTouchMove, { passive: false });
- canvas.addEventListener('touchend', onTouchEnd, { passive: false });
-
- // Update the canvas and walls on window resize.
- window.addEventListener('resize', resize);
-
- // Request sensor permissions if needed (e.g., on iOS).
- requestSensorPermissions();
-
- // Start a continuous update loop that spawns particles when pointer is down.
- spawnLoop();
- }
-
- function spawnLoop() {
- // If the pointer/finger is held down, spawn the configured number of particles.
- 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 onTouchStart(event) {
- event.preventDefault();
- isPointerDown = true;
- const touch = event.touches[0];
- pointerX = touch.clientX;
- pointerY = touch.clientY;
- }
- function onTouchMove(event) {
- event.preventDefault();
- if (isPointerDown && event.touches.length > 0) {
- const touch = event.touches[0];
- pointerX = touch.clientX;
- pointerY = touch.clientY;
- }
- }
- function onTouchEnd(event) {
- event.preventDefault();
- // Stop spawning when no fingers remain on the screen.
- if (event.touches.length === 0) {
- isPointerDown = false;
- pointerX = null;
- pointerY = null;
- }
- }
-
- function createParticle(x, y) {
- // Create a particle with a randomized radius.
- const radius = CONFIG.PARTICLE_RADIUS + Math.random() * CONFIG.PARTICLE_VARIATION;
- const particle = Matter.Bodies.circle(
- x, y, radius, {
- restitution: CONFIG.PARTICLE_RESTITUTION,
- friction: CONFIG.PARTICLE_FRICTION,
- // Air friction helps particles slow down over time.
- frictionAir: CONFIG.PARTICLE_FRICTION_AIR,
- render: {
- // Use the randomNeonColor function to generate a neon color.
- fillStyle: randomNeonColor()
- }
- }
- );
-
- // Apply an initial random force if configured (here SPAWN_FORCE is set to 0).
- if (CONFIG.SPAWN_FORCE !== 0) {
- Matter.Body.applyForce(particle, particle.position, {
- x: (Math.random() - 0.5) * CONFIG.SPAWN_FORCE,
- y: (Math.random() - 0.5) * CONFIG.SPAWN_FORCE
- });
- }
-
- Matter.World.add(engine.world, particle);
- }
-
- function createWalls() {
- // Remove existing walls before creating new ones.
- Matter.World.remove(engine.world, walls);
-
- const wallOptions = {
- isStatic: true,
- restitution: CONFIG.WALL_RESTITUTION,
- render: { fillStyle: '#333333' }
- };
-
- walls = [
- // Top wall.
- Matter.Bodies.rectangle(
- canvas.width / 2,
- -CONFIG.WALL_THICKNESS / 2,
- canvas.width + 100,
- CONFIG.WALL_THICKNESS,
- wallOptions
- ),
- // Bottom wall.
- Matter.Bodies.rectangle(
- canvas.width / 2,
- canvas.height + CONFIG.WALL_THICKNESS / 2,
- canvas.width + 100,
- CONFIG.WALL_THICKNESS,
- wallOptions
- ),
- // Left wall.
- Matter.Bodies.rectangle(
- -CONFIG.WALL_THICKNESS / 2,
- canvas.height / 2,
- CONFIG.WALL_THICKNESS,
- canvas.height + 100,
- wallOptions
- ),
- // Right wall.
- Matter.Bodies.rectangle(
- canvas.width + CONFIG.WALL_THICKNESS / 2,
- canvas.height / 2,
- CONFIG.WALL_THICKNESS,
- canvas.height + 100,
- wallOptions
- )
- ];
-
- Matter.World.add(engine.world, walls);
- }
-
- function resize() {
- canvas.width = window.innerWidth;
- canvas.height = window.innerHeight;
- createWalls();
- }
-
- let lastShake = Date.now();
-
- function handleMotion(e) {
- const acc = e.accelerationIncludingGravity;
- if (!acc) return;
-
- // Multiply raw acceleration by GRAVITY_SCALE to moderate the effect.
- let ax = -acc.x * CONFIG.GRAVITY_SCALE;
- let ay = acc.y * CONFIG.GRAVITY_SCALE;
-
- // Adjust for landscape orientation if needed.
- if (window.innerWidth > window.innerHeight) {
- [ax, ay] = [ay, -ax];
- }
-
- // Normalize the acceleration vector.
- const mag = Math.sqrt(ax * ax + ay * ay);
- let nx = 0, ny = 0;
- if (mag > 0) {
- nx = ax / mag;
- ny = ay / mag;
- }
- // Apply the adjusted gravity. This is combined with the default WORLD_GRAVITY.
- engine.world.gravity.x = nx * 0.5;
- engine.world.gravity.y = ny * 0.5;
-
- // Check for shake events using the increased threshold.
- const now = Date.now();
- if (now - lastShake > 1000 &&
- (Math.abs(acc.x) > CONFIG.SHAKE_THRESHOLD || Math.abs(acc.y) > CONFIG.SHAKE_THRESHOLD)) {
-
- Matter.Composite.allBodies(engine.world).forEach(body => {
- if (!body.isStatic) {
- // Apply a random velocity boost on shake.
- const rx = (Math.random() - 0.5) * CONFIG.SHAKE_FORCE * 50;
- const ry = (Math.random() - 0.5) * CONFIG.SHAKE_FORCE * 50;
- Matter.Body.setVelocity(body, {
- x: body.velocity.x + rx,
- y: body.velocity.y + ry
- });
- }
- });
- lastShake = now;
- // Vibrate if supported.
- if (navigator.vibrate) {
- navigator.vibrate(100);
- }
- }
- }
-
- function requestSensorPermissions() {
- if (typeof DeviceMotionEvent !== "undefined" && typeof DeviceMotionEvent.requestPermission === "function") {
- DeviceMotionEvent.requestPermission().then(state => {
- if (state === "granted") {
- window.addEventListener("devicemotion", handleMotion);
- statusDiv.textContent = "";
- } else {
- statusDiv.textContent = "Sensor permission denied. Minimal gravity effect.";
- }
- }).catch(console.error);
- } else {
- // For browsers that don't require permission.
- window.addEventListener("devicemotion", handleMotion);
- statusDiv.textContent = "";
- }
- }
-
- // Immediately initialize the simulation on page load.
- init();
- </script>
-</body>
-</html>
-
const radius = 1000;
const baseScale = 1.2;
let currentIndex = 10; // starting position on wheel
- const baseSpeed = 0.0035;
+ const baseSpeed = 0.0010;
const dragSensitivity = 0.001; // How much the wheel follows your finger
const damping = 0.985;
const maxAngularVelocity = 0.08;
{ type: "blank", lines: [{ text: "", size: 100 }] },
{ type: "credit", lines: [
{ text: "NEON REALMS", size: 32 },
- { text: "by cassieko", size: 24 }
+ { text: "by cassieko", size: 24 },
+ { text: "and radic", size: 24 }
], spacing: -15 },
{ type: "blank", lines: [{ text: "", size: 100 }] },
{ type: "blank", lines: [{ text: "", size: 100 }] },
{ type: "blank", lines: [{ text: "", size: 100 }] },
{ type: "blank", lines: [{ text: "", size: 100 }] },
{ type: "blank", lines: [{ text: "", size: 100 }] },
+ { type: "credit", lines: [
+ { text: "Cosmos 3D", size: 28 },
+ { text: "by radic", size: 20 },
+ ], spacing: -15 },
+ { type: "blank", lines: [{ text: "", size: 100 }] },
+ { type: "blank", lines: [{ text: "", size: 100 }] },
+ { type: "blank", lines: [{ text: "", size: 100 }] },
+ { type: "blank", lines: [{ text: "", size: 100 }] },
+ { type: "blank", lines: [{ text: "", size: 100 }] },
+ { type: "blank", lines: [{ text: "", size: 100 }] },
{ type: "credit", lines: [
{ text: "Credits Page", size: 28 },
{ text: "~lol meta~", size: 10},