JSFiddle - React, Tailwind, and code Playground

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/howler/2.2.4/howler.min.js"></script>
<html>
  <body>
    <canvas id="canvas"></canvas>
    <div id="boingCount"></div>
  </body>
</html>

JavaScript

class BoingSpringToy {
    constructor(canvas, options = {}) {
        this.canvas = canvas;
        this.ctx = canvas.getContext('2d');

        // Configurable constants
        this.anchor = { x: 17, y: 200 };                    // fixed wall anchor
        this.restLength = options.restLength || 250;         // relaxed spring length
        this.maxCanvasWidth = options.maxWidth || 600;
        this.maxCanvasHeight = options.maxHeight || 400;

        // Physics
        this.ball = { x: 0, y: 0 };                         // current ball position
        this.velocity = { x: 0, y: 0 };                     // current velocity
        this.springFactor = 0.95;       // how strongly it pulls back (0.95 = gentle)
        this.damping = 0.88;            // friction per frame (0.88 = bouncy)
        this.noiseStrength = 0.1;       // tiny randomness for wobble

        // Interaction state
        this.isDragging = false;
        this.dragPoint = { x: 0, y: 0 };

        // Audio (Howler.js must be loaded globally)
        this.sound = null;
        this.soundEnabled = false;
        this.boingCount = parseInt(localStorage.getItem("boingCount") || "0");

        // Visual tweaks
        this.coilFrequency = 25;        // how many loops in the spring
        this.coilAmplitude = 25;        // base thickness of coil wave

        this._initCanvas();
        this._initAudio();
        this._resetBall();
        this._bindEvents();
        this._loop();
    }

    _initCanvas() {
        this.resize();
        window.addEventListener('resize', () => this.resize());
    }

    resize() {
        const width = Math.min(window.innerWidth * 0.9 - 24, this.maxCanvasWidth);
        this.canvas.width = width;
        this.canvas.height = this.maxCanvasHeight;

        // Recenter anchor vertically and update rest position
        this.anchor.y = this.canvas.height / 2;
        this.restLength = Math.min((this.canvas.width -...