{"_id":"retro-pong-game","_rev":"32-bed34a61930168cd4b37e1cdb9c16317","name":"retro-pong-game","dist-tags":{"latest":"6.11.0"},"versions":{"1.1.0":{"name":"retro-pong-game","version":"1.1.0","keywords":["pong","pong game","retro-game","ping-pong","canvas","browser-game","game"],"author":{"url":"https://www.chriscreativecode.com","name":"Chris Schardijn","email":"chriscreativecode@gmail.com"},"license":"MIT","_id":"retro-pong-game@1.1.0","maintainers":[{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"}],"dist":{"shasum":"02790c65c87e7782c21c3900359840b47f907061","tarball":"https://registry.npmjs.org/retro-pong-game/-/retro-pong-game-1.1.0.tgz","fileCount":5,"integrity":"sha512-0hjbBgmS4yUS2Tn0sZRGGtUgmRMe4Tg9tPKfVJYytoE6w6opYEtYo9GyKrzduafX6aXwHFx9zSupyEJ1MgfQUw==","signatures":[{"sig":"MEYCIQC+zQouD2F2qcIaJVvL6s4KhJoqQwRvVeqmwc9bkXj7QgIhAJ+KbqMald4A4ah9H7ik8L/ms+dXULeGrfAqexiwGYUR","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":551707},"main":"dist/pong-game.umd.js","type":"module","types":"dist/pong-game.d.ts","module":"dist/pong-game.es.js","exports":{".":{"types":"./dist/pong-game.d.ts","import":"./dist/pong-game.es.js","require":"./dist/pong-game.umd.js"}},"gitHead":"5dd275111f9704d052c8091760fd0e188c070062","scripts":{"dev":"vite --config vite.demo.config.js","build":"vite build","preview":"vite preview","build:demo":"vite build --config vite.demo.config.js","prepublishOnly":"npm run build"},"_npmUser":{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"},"deprecated":"Renamed to retro-paddle: npm i retro-paddle. Same game, PongGame is now PaddleGame. Migration guide: https://www.npmjs.com/package/retro-paddle#migrating-from-retro-pong-game","_npmVersion":"10.9.2","description":"A retro-style Pong game for the browser. You can drop it onto any web page in a easy way.","directories":{},"_nodeVersion":"22.14.0","dependencies":{"sound-manager-ts":"^5.5.7"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.0.5"},"_npmOperationalInternal":{"tmp":"tmp/retro-pong-game_1.1.0_1779278784271_0.5595760682489053","host":"s3://npm-registry-packages-npm-production"}},"1.1.1":{"name":"retro-pong-game","version":"1.1.1","keywords":["pong","pong game","retro-game","ping-pong","canvas","browser-game","game"],"author":{"url":"https://www.chriscreativecode.com","name":"Chris Schardijn","email":"chriscreativecode@gmail.com"},"license":"MIT","_id":"retro-pong-game@1.1.1","maintainers":[{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"}],"dist":{"shasum":"1328e56afbce8b426ddfae97f0fa5ec25d565e1d","tarball":"https://registry.npmjs.org/retro-pong-game/-/retro-pong-game-1.1.1.tgz","fileCount":5,"integrity":"sha512-LMRbiRqgXUdnh44IQB6zMkRrF/APyoXIZv0t1TP7cTYkScFxqEaWG9EZL9OYiSk8FjCIOwE8yPQxRGbpffKpag==","signatures":[{"sig":"MEUCIQDgrFDA0UlvSY/A83S81iFfohAvFPVdry2e0kqJvhT/TQIgYZV6DcsekiGVjRV7PXanMpYktCbNA4BDK8me6BiJS7s=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":551772},"main":"dist/pong-game.umd.js","type":"module","types":"dist/pong-game.d.ts","module":"dist/pong-game.es.js","exports":{".":{"types":"./dist/pong-game.d.ts","import":"./dist/pong-game.es.js","require":"./dist/pong-game.umd.js"}},"gitHead":"5dd275111f9704d052c8091760fd0e188c070062","scripts":{"dev":"vite --config vite.demo.config.js","build":"vite build","preview":"vite preview","build:demo":"vite build --config vite.demo.config.js","prepublishOnly":"npm run build"},"_npmUser":{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"},"deprecated":"Renamed to retro-paddle: npm i retro-paddle. Same game, PongGame is now PaddleGame. Migration guide: https://www.npmjs.com/package/retro-paddle#migrating-from-retro-pong-game","_npmVersion":"10.9.2","description":"A retro-style Pong game for the browser. You can drop it onto any web page in a easy way.","directories":{},"_nodeVersion":"22.14.0","dependencies":{"sound-manager-ts":"^5.5.7"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.0.5"},"_npmOperationalInternal":{"tmp":"tmp/retro-pong-game_1.1.1_1779283011641_0.8096368798794367","host":"s3://npm-registry-packages-npm-production"}},"1.1.2":{"name":"retro-pong-game","version":"1.1.2","keywords":["pong","pong game","retro-game","ping-pong","canvas","browser-game","game"],"author":{"url":"https://www.chriscreativecode.com","name":"Chris Schardijn","email":"chriscreativecode@gmail.com"},"license":"MIT","_id":"retro-pong-game@1.1.2","maintainers":[{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"}],"homepage":"https://bitbucket.org/chriscrossweb/pong-game-vite#readme","bugs":{"url":"https://bitbucket.org/chriscrossweb/pong-game-vite/issues"},"dist":{"shasum":"557a2350edad9eb8a382597dd62e4d19422147c3","tarball":"https://registry.npmjs.org/retro-pong-game/-/retro-pong-game-1.1.2.tgz","fileCount":6,"integrity":"sha512-FUMh6GAWR0WCr92J4E9E1cOFWLadEVfwcjCqwMAF1Tz4PVOl7e6w8vR2b9LQH8ZKBLgH4agR/lvAwtC6YgCTgA==","signatures":[{"sig":"MEYCIQDZdnVmyhKsCDBW6QiwETS9xkdipGJBAWouTdUCyjDVOQIhANQy1gEzQpPb0nbYXduKZtp+zXZn8DHXOtYtDzCE3xLm","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":629486},"main":"dist/pong-game.umd.js","type":"module","types":"dist/pong-game.d.ts","module":"dist/pong-game.es.js","exports":{".":{"types":"./dist/pong-game.d.ts","import":"./dist/pong-game.es.js","require":"./dist/pong-game.umd.js"}},"gitHead":"cabd2d629cf71f2346012d6257ce92fd3c9bdd13","scripts":{"dev":"vite --config vite.demo.config.js","build":"vite build","preview":"vite preview","build:demo":"vite build --config vite.demo.config.js","prepublishOnly":"npm run build"},"_npmUser":{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"},"deprecated":"Renamed to retro-paddle: npm i retro-paddle. Same game, PongGame is now PaddleGame. Migration guide: https://www.npmjs.com/package/retro-paddle#migrating-from-retro-pong-game","repository":{"url":"git+https://bitbucket.org/chriscrossweb/pong-game-vite.git","type":"git"},"_npmVersion":"10.9.2","description":"A retro-style Pong game for the browser. You can drop it onto any web page in a easy way.","directories":{},"_nodeVersion":"22.14.0","dependencies":{"sound-manager-ts":"^5.5.7"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.0.5"},"_npmOperationalInternal":{"tmp":"tmp/retro-pong-game_1.1.2_1779286569562_0.3217988729536563","host":"s3://npm-registry-packages-npm-production"}},"1.1.3":{"name":"retro-pong-game","version":"1.1.3","keywords":["pong","pong game","retro-game","ping-pong","canvas","browser-game","game"],"author":{"url":"https://www.chriscreativecode.com","name":"Chris Schardijn","email":"chriscreativecode@gmail.com"},"license":"MIT","_id":"retro-pong-game@1.1.3","maintainers":[{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"}],"homepage":"https://bitbucket.org/chriscrossweb/pong-game-vite#readme","bugs":{"url":"https://bitbucket.org/chriscrossweb/pong-game-vite/issues"},"dist":{"shasum":"f73740033a76e143a0fa2f16fc08af90cafa373d","tarball":"https://registry.npmjs.org/retro-pong-game/-/retro-pong-game-1.1.3.tgz","fileCount":5,"integrity":"sha512-G7WnQTvZTAydxgxfWA+/fI1A11naaitMr3xSm74CqoUE7RaJa0WXykpgvf+xsndswSSEafoYReFs7iLG7VmgOA==","signatures":[{"sig":"MEYCIQC2M72vvZvEefH+P2DvINprxbhVMGinwus64cDg7YvwXQIhANc0y+PSDjicoBCOAzabdft5wzb5vv6s2hSdZICVzWCX","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":551995},"main":"dist/pong-game.umd.js","type":"module","types":"dist/pong-game.d.ts","module":"dist/pong-game.es.js","exports":{".":{"types":"./dist/pong-game.d.ts","import":"./dist/pong-game.es.js","require":"./dist/pong-game.umd.js"}},"gitHead":"0311cfb29a3c367914181f902fffc3b068aafe38","scripts":{"dev":"vite --config vite.demo.config.js","build":"vite build","preview":"vite preview","build:demo":"vite build --config vite.demo.config.js","prepublishOnly":"npm run build"},"_npmUser":{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"},"deprecated":"Renamed to retro-paddle: npm i retro-paddle. Same game, PongGame is now PaddleGame. Migration guide: https://www.npmjs.com/package/retro-paddle#migrating-from-retro-pong-game","repository":{"url":"git+https://bitbucket.org/chriscrossweb/pong-game-vite.git","type":"git"},"_npmVersion":"10.9.2","description":"A retro-style Pong game for the browser. You can drop it onto any web page in a easy way.","directories":{},"_nodeVersion":"22.14.0","dependencies":{"sound-manager-ts":"^5.5.7"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.0.5"},"_npmOperationalInternal":{"tmp":"tmp/retro-pong-game_1.1.3_1779287303300_0.9267620226275479","host":"s3://npm-registry-packages-npm-production"}},"2.0.0":{"name":"retro-pong-game","version":"2.0.0","keywords":["pong","pong game","retro-game","ping-pong","canvas","browser-game","game"],"author":{"url":"https://www.chriscreativecode.com","name":"Chris Schardijn","email":"chriscreativecode@gmail.com"},"license":"MIT","_id":"retro-pong-game@2.0.0","maintainers":[{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"}],"homepage":"https://bitbucket.org/chriscrossweb/pong-game-vite#readme","bugs":{"url":"https://bitbucket.org/chriscrossweb/pong-game-vite/issues"},"dist":{"shasum":"c01df0a0f82bb6b3a8cc38ae5fd60f113dce3c56","tarball":"https://registry.npmjs.org/retro-pong-game/-/retro-pong-game-2.0.0.tgz","fileCount":5,"integrity":"sha512-2zD7iTumUn76qK2kgmFUiJgYLNXnHLwPp7N/T177VyqIMm/BHvOG6FJjdgisAf2NrYznVInri7AvL2qp9AatBw==","signatures":[{"sig":"MEUCIE1zQvLqZlof8MO8U3ZxJZw02FswNZOh463bNar4XOrgAiEAnaRbL7vXUgvANJaY9kKLTtmnK9kfmp9s6elUHtM3YIc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":562015},"main":"dist/pong-game.umd.js","type":"module","types":"dist/pong-game.d.ts","module":"dist/pong-game.es.js","exports":{".":{"types":"./dist/pong-game.d.ts","import":"./dist/pong-game.es.js","require":"./dist/pong-game.umd.js"}},"gitHead":"6acf6df848f6a65e91e691d65d65bf325ae4e763","scripts":{"dev":"vite --config vite.demo.config.js","build":"vite build","preview":"vite preview","build:demo":"vite build --config vite.demo.config.js","prepublishOnly":"npm run build"},"_npmUser":{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"},"deprecated":"Renamed to retro-paddle: npm i retro-paddle. Same game, PongGame is now PaddleGame. Migration guide: https://www.npmjs.com/package/retro-paddle#migrating-from-retro-pong-game","repository":{"url":"git+https://bitbucket.org/chriscrossweb/pong-game-vite.git","type":"git"},"_npmVersion":"10.9.2","description":"A retro-style Pong game for the browser. You can drop it onto any web page in a easy way.","directories":{},"_nodeVersion":"22.14.0","dependencies":{"sound-manager-ts":"^5.5.7"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.0.5"},"_npmOperationalInternal":{"tmp":"tmp/retro-pong-game_2.0.0_1779349004082_0.7517615799109529","host":"s3://npm-registry-packages-npm-production"}},"3.0.0":{"name":"retro-pong-game","version":"3.0.0","keywords":["pong","pong game","retro-game","ping-pong","canvas","browser-game","game"],"author":{"url":"https://www.chriscreativecode.com","name":"Chris Schardijn","email":"chriscreativecode@gmail.com"},"license":"MIT","_id":"retro-pong-game@3.0.0","maintainers":[{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"}],"homepage":"https://bitbucket.org/chriscrossweb/pong-game-vite#readme","bugs":{"url":"https://bitbucket.org/chriscrossweb/pong-game-vite/issues"},"dist":{"shasum":"24c0f1d59ba089aa2d7baa5a69950eb0371a713a","tarball":"https://registry.npmjs.org/retro-pong-game/-/retro-pong-game-3.0.0.tgz","fileCount":5,"integrity":"sha512-iBsivytiP/THax8vDZEZclcErRrXZUQYoDZkO6bOI/WGEyB31WJ24Luuc+ZM23k1JMsHmM8dDGp7AZoIc1xA8Q==","signatures":[{"sig":"MEUCIHawL++Xky7ORNbjxOHL1BlffmQ1G0ZVedDeCtKWYh/QAiEAqAmSaqXkQq5c/3gd8DID9yGbSNPcL6r+c+cm+ztWV34=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":578997},"main":"dist/pong-game.umd.js","type":"module","types":"dist/pong-game.d.ts","module":"dist/pong-game.es.js","exports":{".":{"types":"./dist/pong-game.d.ts","import":"./dist/pong-game.es.js","require":"./dist/pong-game.umd.js"}},"gitHead":"d64720839854e3ed73a2c3395ac0d2aafdf24b6f","scripts":{"dev":"vite --config vite.demo.config.js","build":"vite build","preview":"vite preview","build:demo":"vite build --config vite.demo.config.js","prepublishOnly":"npm run build"},"_npmUser":{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"},"deprecated":"Renamed to retro-paddle: npm i retro-paddle. Same game, PongGame is now PaddleGame. Migration guide: https://www.npmjs.com/package/retro-paddle#migrating-from-retro-pong-game","repository":{"url":"git+https://bitbucket.org/chriscrossweb/pong-game-vite.git","type":"git"},"_npmVersion":"10.9.2","description":"A retro-style Pong game for the browser. You can drop it onto any web page in a easy way.","directories":{},"_nodeVersion":"22.14.0","dependencies":{"sound-manager-ts":"^5.5.7"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.0.5"},"_npmOperationalInternal":{"tmp":"tmp/retro-pong-game_3.0.0_1779381944679_0.04982887611524278","host":"s3://npm-registry-packages-npm-production"}},"3.0.1":{"name":"retro-pong-game","version":"3.0.1","keywords":["pong","pong game","retro-game","ping-pong","canvas","browser-game","game"],"author":{"url":"https://www.chriscreativecode.com","name":"Chris Schardijn","email":"chriscreativecode@gmail.com"},"license":"MIT","_id":"retro-pong-game@3.0.1","maintainers":[{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"}],"homepage":"https://bitbucket.org/chriscrossweb/pong-game-vite#readme","bugs":{"url":"https://bitbucket.org/chriscrossweb/pong-game-vite/issues"},"dist":{"shasum":"d4ed9616960329724bda985a678c84c1266972bc","tarball":"https://registry.npmjs.org/retro-pong-game/-/retro-pong-game-3.0.1.tgz","fileCount":5,"integrity":"sha512-7xB7ov9xwHOZUExLd1/bZmLmDdBJpL669dQ7jRUoPCp4efP+IdD0ftb6C5RetaZAShNJHN+Q3rFVuW1CvX5MRg==","signatures":[{"sig":"MEUCIBTfNCFb2vE9OkRibX6BYbHq8FmXXx3nPQiKdCXEfJ9DAiEAlP3qV14FMSitswnNbqTE4fHNhtPpwamR3ddCYEZuDlI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":579000},"main":"dist/pong-game.umd.js","type":"module","types":"dist/pong-game.d.ts","module":"dist/pong-game.es.js","exports":{".":{"types":"./dist/pong-game.d.ts","import":"./dist/pong-game.es.js","require":"./dist/pong-game.umd.js"}},"gitHead":"8aedbdf206d302437fbac4685f5f2adfa6e90654","scripts":{"dev":"vite --config vite.demo.config.js","build":"vite build","preview":"vite preview","build:demo":"vite build --config vite.demo.config.js","prepublishOnly":"npm run build"},"_npmUser":{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"},"deprecated":"Renamed to retro-paddle: npm i retro-paddle. Same game, PongGame is now PaddleGame. Migration guide: https://www.npmjs.com/package/retro-paddle#migrating-from-retro-pong-game","repository":{"url":"git+https://bitbucket.org/chriscrossweb/pong-game-vite.git","type":"git"},"_npmVersion":"10.9.2","description":"A retro-style Pong game for the browser. You can drop it onto any web page in a easy way.","directories":{},"_nodeVersion":"22.14.0","dependencies":{"sound-manager-ts":"^5.5.7"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.0.5"},"_npmOperationalInternal":{"tmp":"tmp/retro-pong-game_3.0.1_1779382803089_0.9796549485945993","host":"s3://npm-registry-packages-npm-production"}},"4.0.0":{"name":"retro-pong-game","version":"4.0.0","keywords":["pong","pong game","retro-game","ping-pong","canvas","browser-game","game"],"author":{"url":"https://www.chriscreativecode.com","name":"Chris Schardijn","email":"chriscreativecode@gmail.com"},"license":"MIT","_id":"retro-pong-game@4.0.0","maintainers":[{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"}],"homepage":"https://bitbucket.org/chriscrossweb/pong-game-vite#readme","bugs":{"url":"https://bitbucket.org/chriscrossweb/pong-game-vite/issues"},"dist":{"shasum":"a1951edc764eb1ede8233597b148fd00f951bdfb","tarball":"https://registry.npmjs.org/retro-pong-game/-/retro-pong-game-4.0.0.tgz","fileCount":5,"integrity":"sha512-bqHfQRHyRjN/HB2ci5e0pgtdsRtqY2ZzxRMMZBk0y66X9siHMXr5sj4RrDslfSB2e1pmA/S9CkwRTKdZ8AeI+g==","signatures":[{"sig":"MEYCIQCDAZ8GQyPPtuqGElSWVxFyHJQlKhXgUAUuMqh39ijrIwIhAPpBaU5fsThohIXwDtA2E+zCYunKtRSmFNUFBm0VUhf4","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1435027},"main":"dist/pong-game.umd.js","type":"module","types":"dist/pong-game.d.ts","module":"dist/pong-game.es.js","exports":{".":{"types":"./dist/pong-game.d.ts","import":"./dist/pong-game.es.js","require":"./dist/pong-game.umd.js"}},"gitHead":"1565523eb221205a98a56ca007ecfcfb0d14dea1","scripts":{"dev":"vite --config vite.demo.config.js","build":"vite build","preview":"vite preview","build:demo":"vite build --config vite.demo.config.js","prepublishOnly":"npm run build"},"_npmUser":{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"},"deprecated":"Renamed to retro-paddle: npm i retro-paddle. Same game, PongGame is now PaddleGame. Migration guide: https://www.npmjs.com/package/retro-paddle#migrating-from-retro-pong-game","repository":{"url":"git+https://bitbucket.org/chriscrossweb/pong-game-vite.git","type":"git"},"_npmVersion":"10.9.2","description":"A retro-style Pong game for the browser. You can drop it onto any web page in a easy way.","directories":{},"_nodeVersion":"22.14.0","dependencies":{"sound-manager-ts":"^5.5.7"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.0.5","playwright":"^1.60.0"},"_npmOperationalInternal":{"tmp":"tmp/retro-pong-game_4.0.0_1779455322969_0.12471946927910893","host":"s3://npm-registry-packages-npm-production"}},"4.0.1":{"name":"retro-pong-game","version":"4.0.1","keywords":["pong","pong game","retro-game","ping-pong","canvas","browser-game","game"],"author":{"url":"https://www.chriscreativecode.com","name":"Chris Schardijn","email":"chriscreativecode@gmail.com"},"license":"MIT","_id":"retro-pong-game@4.0.1","maintainers":[{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"}],"homepage":"https://bitbucket.org/chriscrossweb/pong-game-vite#readme","bugs":{"url":"https://bitbucket.org/chriscrossweb/pong-game-vite/issues"},"dist":{"shasum":"990b0fb3797f081597cd1236048d3aebdd1f8a31","tarball":"https://registry.npmjs.org/retro-pong-game/-/retro-pong-game-4.0.1.tgz","fileCount":5,"integrity":"sha512-hFaDVFHX/T+85k4QqhrqPrwcBhUo05lT7rz5v6NbvswovzwE0N/H9k6/khbQ+uLcLOsAeS3HGLwSo6h//jGmWA==","signatures":[{"sig":"MEUCIC/IuHSqsyOciULBETiuejnY4NXwhnYFvPVdkfyWNgH5AiEA+V+yKf/+X5Y3pLhkF+aJV5o/Bbv9gGt7EJTMqTEZfmk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1435883},"main":"dist/pong-game.umd.js","type":"module","types":"dist/pong-game.d.ts","module":"dist/pong-game.es.js","exports":{".":{"types":"./dist/pong-game.d.ts","import":"./dist/pong-game.es.js","require":"./dist/pong-game.umd.js"}},"gitHead":"dbd2dd69af7a25e2e8975d0e0ffbac7a2b381174","scripts":{"dev":"vite --config vite.demo.config.js","build":"vite build","preview":"vite preview","build:demo":"vite build --config vite.demo.config.js","prepublishOnly":"npm run build"},"_npmUser":{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"},"deprecated":"Renamed to retro-paddle: npm i retro-paddle. Same game, PongGame is now PaddleGame. Migration guide: https://www.npmjs.com/package/retro-paddle#migrating-from-retro-pong-game","repository":{"url":"git+https://bitbucket.org/chriscrossweb/pong-game-vite.git","type":"git"},"_npmVersion":"10.9.2","description":"A retro-style Pong game for the browser. You can drop it onto any web page in a easy way.","directories":{},"_nodeVersion":"22.14.0","dependencies":{"sound-manager-ts":"^5.5.7"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.0.5","playwright":"^1.60.0"},"_npmOperationalInternal":{"tmp":"tmp/retro-pong-game_4.0.1_1779455947532_0.2493135262337225","host":"s3://npm-registry-packages-npm-production"}},"4.1.0":{"name":"retro-pong-game","version":"4.1.0","keywords":["pong","pong game","retro-game","ping-pong","canvas","browser-game","game"],"author":{"url":"https://www.chriscreativecode.com","name":"Chris Schardijn","email":"chriscreativecode@gmail.com"},"license":"MIT","_id":"retro-pong-game@4.1.0","maintainers":[{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"}],"homepage":"https://bitbucket.org/chriscrossweb/pong-game-vite#readme","bugs":{"url":"https://bitbucket.org/chriscrossweb/pong-game-vite/issues"},"dist":{"shasum":"d72f8d268d70ce414d18ae71fea101a806658807","tarball":"https://registry.npmjs.org/retro-pong-game/-/retro-pong-game-4.1.0.tgz","fileCount":19,"integrity":"sha512-iPbzEeypFE615yqkWg4e2FzX3gU7/fg0CJzE2xUNn/7tUZToKOvowHCExKrT4aZUg5kuNNpnM0fGAJ9Z3ybeqQ==","signatures":[{"sig":"MEQCIFqapX7aLLnvLJHanMpWosYeu/Qk778XYJt9KOtn8IaTAiAJKVGiIaQicf39yvMT0Ql3NZ6U/jWgbRSq0QMRzCYjew==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":679617},"main":"dist/pong-game.umd.js","type":"module","types":"dist/pong-game.d.ts","module":"dist/pong-game.es.js","exports":{".":{"types":"./dist/pong-game.d.ts","import":"./dist/pong-game.es.js","require":"./dist/pong-game.umd.js"}},"gitHead":"8b958517a66f82c53c72986190d67b514a5dec9b","scripts":{"dev":"vite --config vite.demo.config.js","build":"vite build","preview":"vite preview","build:demo":"vite build --config vite.demo.config.js","prepublishOnly":"npm run build"},"_npmUser":{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"},"deprecated":"Renamed to retro-paddle: npm i retro-paddle. Same game, PongGame is now PaddleGame. Migration guide: https://www.npmjs.com/package/retro-paddle#migrating-from-retro-pong-game","repository":{"url":"git+https://bitbucket.org/chriscrossweb/pong-game-vite.git","type":"git"},"_npmVersion":"10.9.2","description":"A retro-style Pong game for the browser. You can drop it onto any web page in a easy way.","directories":{},"_nodeVersion":"22.14.0","dependencies":{"sound-manager-ts":"^5.5.7"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.0.5","playwright":"^1.60.0"},"_npmOperationalInternal":{"tmp":"tmp/retro-pong-game_4.1.0_1779524152349_0.8535309267802358","host":"s3://npm-registry-packages-npm-production"}},"4.1.1":{"name":"retro-pong-game","version":"4.1.1","keywords":["pong","pong game","retro-game","ping-pong","canvas","browser-game","game"],"author":{"url":"https://www.chriscreativecode.com","name":"Chris Schardijn","email":"chriscreativecode@gmail.com"},"license":"MIT","_id":"retro-pong-game@4.1.1","maintainers":[{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"}],"homepage":"https://bitbucket.org/chriscrossweb/pong-game-vite#readme","bugs":{"url":"https://bitbucket.org/chriscrossweb/pong-game-vite/issues"},"dist":{"shasum":"8427ca1ead8744e35879b20409a5fbb9d37d1bd1","tarball":"https://registry.npmjs.org/retro-pong-game/-/retro-pong-game-4.1.1.tgz","fileCount":19,"integrity":"sha512-fhEN+gVBis4B+JcZeMN+qdXoFS/J+YK/mQYJF1mqRTuYmorL6Gj+sEFJ2CD60bb/BM8+E9u1k7hDi8oB3GQA4g==","signatures":[{"sig":"MEYCIQDSGAVkikHSyRGLTNKNRrxDtJIC8IzS8ZakHHZKHIo/1gIhAIu4xwcmHgkHJFCk/i5fnQBksXVKszpyssHFhmeAXRWa","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":679541},"main":"dist/pong-game.umd.js","type":"module","types":"dist/pong-game.d.ts","module":"dist/pong-game.es.js","exports":{".":{"types":"./dist/pong-game.d.ts","import":"./dist/pong-game.es.js","require":"./dist/pong-game.umd.js"}},"gitHead":"cae11931765b60a6fab1c57f772dad1d26d9cd3a","scripts":{"dev":"vite --config vite.demo.config.js","build":"vite build","preview":"vite preview","build:demo":"vite build --config vite.demo.config.js","prepublishOnly":"npm run build"},"_npmUser":{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"},"deprecated":"Renamed to retro-paddle: npm i retro-paddle. Same game, PongGame is now PaddleGame. Migration guide: https://www.npmjs.com/package/retro-paddle#migrating-from-retro-pong-game","repository":{"url":"git+https://bitbucket.org/chriscrossweb/pong-game-vite.git","type":"git"},"_npmVersion":"10.9.2","description":"A retro-style Pong game for the browser. You can drop it onto any web page in a easy way.","directories":{},"_nodeVersion":"22.14.0","dependencies":{"sound-manager-ts":"^5.5.7"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.0.5","playwright":"^1.60.0"},"_npmOperationalInternal":{"tmp":"tmp/retro-pong-game_4.1.1_1779524331175_0.8739614064212498","host":"s3://npm-registry-packages-npm-production"}},"4.2.0":{"name":"retro-pong-game","version":"4.2.0","keywords":["pong","pong game","retro-game","ping-pong","canvas","browser-game","game"],"author":{"url":"https://www.chriscreativecode.com","name":"Chris Schardijn","email":"chriscreativecode@gmail.com"},"license":"MIT","_id":"retro-pong-game@4.2.0","maintainers":[{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"}],"homepage":"https://bitbucket.org/chriscrossweb/pong-game-vite#readme","bugs":{"url":"https://bitbucket.org/chriscrossweb/pong-game-vite/issues"},"dist":{"shasum":"1e5b1ce7f18482762b2082c0050cdc86ea9ca094","tarball":"https://registry.npmjs.org/retro-pong-game/-/retro-pong-game-4.2.0.tgz","fileCount":5,"integrity":"sha512-DUF9OpYupEu9tiEhn3KQCn9eo8qXi1lSVOoQ/Jn+cMJTuGYQIqgyphrj1mQD2lDnpufcpkHYMwIHYhiBej0wNA==","signatures":[{"sig":"MEUCIBFsyLtyOLHZe1yJ5KQLy2ZwZU5HsRdUTUfXUwX85p+WAiEArwLYLWu8L55d4O6owSF9gvJ70pHGi5XZedhmb3TBkVo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":879756},"main":"dist/pong-game.umd.js","type":"module","types":"dist/pong-game.d.ts","module":"dist/pong-game.es.js","exports":{".":{"types":"./dist/pong-game.d.ts","import":"./dist/pong-game.es.js","require":"./dist/pong-game.umd.js"}},"gitHead":"f1da47f8bdd717db49d07c43d44b928ba15b58e0","scripts":{"dev":"vite --config vite.demo.config.js","build":"vite build","preview":"vite preview","build:demo":"vite build --config vite.demo.config.js","prepublishOnly":"npm run build"},"_npmUser":{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"},"deprecated":"Renamed to retro-paddle: npm i retro-paddle. Same game, PongGame is now PaddleGame. Migration guide: https://www.npmjs.com/package/retro-paddle#migrating-from-retro-pong-game","repository":{"url":"git+https://bitbucket.org/chriscrossweb/pong-game-vite.git","type":"git"},"_npmVersion":"10.9.2","description":"A retro-style Pong game for the browser. You can drop it onto any web page in a easy way.","directories":{},"_nodeVersion":"22.14.0","dependencies":{"sound-manager-ts":"^5.5.7"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.0.5","playwright":"^1.60.0"},"_npmOperationalInternal":{"tmp":"tmp/retro-pong-game_4.2.0_1779529423212_0.37997641759036394","host":"s3://npm-registry-packages-npm-production"}},"4.2.1":{"name":"retro-pong-game","version":"4.2.1","keywords":["pong","pong game","retro-game","ping-pong","canvas","browser-game","game"],"author":{"url":"https://www.chriscreativecode.com","name":"Chris Schardijn","email":"chriscreativecode@gmail.com"},"license":"MIT","_id":"retro-pong-game@4.2.1","maintainers":[{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"}],"homepage":"https://bitbucket.org/chriscrossweb/pong-game-vite#readme","bugs":{"url":"https://bitbucket.org/chriscrossweb/pong-game-vite/issues"},"dist":{"shasum":"b8f828bd19313326ef098a810648d1c21454c3d2","tarball":"https://registry.npmjs.org/retro-pong-game/-/retro-pong-game-4.2.1.tgz","fileCount":5,"integrity":"sha512-0i5H54CQDAHo7hbcysbngjgMaKOr4dQ737IMqaCb4rlfIFLipem6/QSlx057/FjFCbb9TX97mn8PFAE9pisd9g==","signatures":[{"sig":"MEUCIQDHQhjXGO4qa9t81grEOJWkWsrUbJiDRdQYYP0fhyzhGgIgD8CnCToX4jmI5LsHrdsdvYpz66nVh88/0afexhvtaiA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":879909},"main":"dist/pong-game.umd.js","type":"module","types":"dist/pong-game.d.ts","module":"dist/pong-game.es.js","exports":{".":{"types":"./dist/pong-game.d.ts","import":"./dist/pong-game.es.js","require":"./dist/pong-game.umd.js"}},"gitHead":"d3af69ab88ce2630051449852965536966e54e52","scripts":{"dev":"vite --config vite.demo.config.js","build":"vite build","preview":"vite preview","build:demo":"vite build --config vite.demo.config.js","prepublishOnly":"npm run build"},"_npmUser":{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"},"deprecated":"Renamed to retro-paddle: npm i retro-paddle. Same game, PongGame is now PaddleGame. Migration guide: https://www.npmjs.com/package/retro-paddle#migrating-from-retro-pong-game","repository":{"url":"git+https://bitbucket.org/chriscrossweb/pong-game-vite.git","type":"git"},"_npmVersion":"10.9.2","description":"A retro-style Pong game for the browser. You can drop it onto any web page in a easy way.","directories":{},"_nodeVersion":"22.14.0","dependencies":{"sound-manager-ts":"^5.5.7"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.0.5","playwright":"^1.60.0"},"_npmOperationalInternal":{"tmp":"tmp/retro-pong-game_4.2.1_1779530086588_0.2817799567626884","host":"s3://npm-registry-packages-npm-production"}},"5.0.0":{"name":"retro-pong-game","version":"5.0.0","keywords":["pong","pong game","retro-game","ping-pong","canvas","browser-game","game"],"author":{"url":"https://www.chriscreativecode.com","name":"Chris Schardijn","email":"chriscreativecode@gmail.com"},"license":"MIT","_id":"retro-pong-game@5.0.0","maintainers":[{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"}],"homepage":"https://bitbucket.org/chriscrossweb/pong-game-vite#readme","bugs":{"url":"https://bitbucket.org/chriscrossweb/pong-game-vite/issues"},"dist":{"shasum":"55d6703376dad52debdb95a58c6a86f351b9bc5b","tarball":"https://registry.npmjs.org/retro-pong-game/-/retro-pong-game-5.0.0.tgz","fileCount":5,"integrity":"sha512-YyeUFdcOcpcUMWvS85VP0Aw3g+CFlVJBYgwy3Bf0LhAnoQYEyt8DGKTUDv3fbUZ9vxKWNLLE745tQWLbsu/WRw==","signatures":[{"sig":"MEQCIFXC/oMqxftHHZ8DTwYIJNW7mnEaL6XX7vX1L7K9i3zZAiBoqk0bonlPwHFGS0IIlddC0GR1eiOp8iQgwDFQ0UWpBQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":929391},"main":"dist/pong-game.umd.js","type":"module","types":"dist/pong-game.d.ts","module":"dist/pong-game.es.js","exports":{".":{"types":"./dist/pong-game.d.ts","import":"./dist/pong-game.es.js","require":"./dist/pong-game.umd.js"}},"gitHead":"afa9a50539add678f18073c603cc70af64ccf89f","scripts":{"dev":"vite --config vite.demo.config.js","build":"vite build","preview":"vite preview","build:demo":"vite build --config vite.demo.config.js","prepublishOnly":"npm run build"},"_npmUser":{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"},"deprecated":"Renamed to retro-paddle: npm i retro-paddle. Same game, PongGame is now PaddleGame. Migration guide: https://www.npmjs.com/package/retro-paddle#migrating-from-retro-pong-game","repository":{"url":"git+https://bitbucket.org/chriscrossweb/pong-game-vite.git","type":"git"},"_npmVersion":"10.9.2","description":"A retro-style Pong game for the browser. You can drop it onto any web page in a easy way.","directories":{},"_nodeVersion":"22.14.0","dependencies":{"sound-manager-ts":"^5.5.7"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.0.5","playwright":"^1.60.0"},"_npmOperationalInternal":{"tmp":"tmp/retro-pong-game_5.0.0_1779881559952_0.5331503308242875","host":"s3://npm-registry-packages-npm-production"}},"5.1.0":{"name":"retro-pong-game","version":"5.1.0","keywords":["pong","pong game","retro-game","ping-pong","canvas","browser-game","game"],"author":{"url":"https://www.chriscreativecode.com","name":"Chris Schardijn","email":"chriscreativecode@gmail.com"},"license":"MIT","_id":"retro-pong-game@5.1.0","maintainers":[{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"}],"homepage":"https://bitbucket.org/chriscrossweb/pong-game-vite#readme","bugs":{"url":"https://bitbucket.org/chriscrossweb/pong-game-vite/issues"},"dist":{"shasum":"1a352bf7f54f13fdff3380ad590d3eaea473e49e","tarball":"https://registry.npmjs.org/retro-pong-game/-/retro-pong-game-5.1.0.tgz","fileCount":5,"integrity":"sha512-ZWMZDXIoRa4B6oStbPtHMcRaJ17ejU/auCjmUSBdUOCh9fBNmelYFbgnOAdcuLooZEVKTi87/o/KspBBZmtQUw==","signatures":[{"sig":"MEUCIQDNc5BfCzhjj49Bg/6Z19a6NcwwO5fQVtqK2dPp9AMBsgIgemEof93/d7XlwJTJUQvrtzQ4xBaepszrLAxb7A60tcM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":951895},"main":"dist/pong-game.umd.js","type":"module","types":"dist/pong-game.d.ts","module":"dist/pong-game.es.js","exports":{".":{"types":"./dist/pong-game.d.ts","import":"./dist/pong-game.es.js","require":"./dist/pong-game.umd.js"}},"gitHead":"1cafb5cc2993bf25ad9a3d72b59fd82c88ac1b7a","scripts":{"dev":"vite --config vite.demo.config.js","build":"vite build","preview":"vite preview","build:demo":"vite build --config vite.demo.config.js","prepublishOnly":"npm run build"},"_npmUser":{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"},"deprecated":"Renamed to retro-paddle: npm i retro-paddle. Same game, PongGame is now PaddleGame. Migration guide: https://www.npmjs.com/package/retro-paddle#migrating-from-retro-pong-game","repository":{"url":"git+https://bitbucket.org/chriscrossweb/pong-game-vite.git","type":"git"},"_npmVersion":"10.9.2","description":"A retro-style Pong game for the browser. You can drop it onto any web page in a easy way.","directories":{},"_nodeVersion":"22.14.0","dependencies":{"sound-manager-ts":"^5.5.7"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.0.5","playwright":"^1.60.0"},"_npmOperationalInternal":{"tmp":"tmp/retro-pong-game_5.1.0_1780143757616_0.84828071730222","host":"s3://npm-registry-packages-npm-production"}},"5.1.1":{"name":"retro-pong-game","version":"5.1.1","keywords":["pong","pong game","retro-game","ping-pong","canvas","browser-game","game"],"author":{"url":"https://www.chriscreativecode.com","name":"Chris Schardijn","email":"chriscreativecode@gmail.com"},"license":"MIT","_id":"retro-pong-game@5.1.1","maintainers":[{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"}],"homepage":"https://bitbucket.org/chriscrossweb/pong-game-vite#readme","bugs":{"url":"https://bitbucket.org/chriscrossweb/pong-game-vite/issues"},"dist":{"shasum":"201c6111963e73f88dfb1c4be343d9a68b73a41d","tarball":"https://registry.npmjs.org/retro-pong-game/-/retro-pong-game-5.1.1.tgz","fileCount":5,"integrity":"sha512-P32lZTcLwmsPAcBx7Z9mvg+ODTKRO0vSJiM9oEvnaTxvuvbJfnrbm5t5MxDD6OVLDZ23A0nIhFEW+RSqGmu8Mg==","signatures":[{"sig":"MEQCIEP69ToenHJ0XfiRboNwL0sqt91FW5KEgajDKP2wfqIHAiAjaF+KVORw68ns2IazbywxmzeMz5lizufd/ER+0x/Q/g==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":953429},"main":"dist/pong-game.umd.js","type":"module","types":"dist/pong-game.d.ts","module":"dist/pong-game.es.js","exports":{".":{"types":"./dist/pong-game.d.ts","import":"./dist/pong-game.es.js","require":"./dist/pong-game.umd.js"}},"gitHead":"d61caa9acdda06a164a0fc10c60bc3c9bc4a703c","scripts":{"dev":"vite --config vite.demo.config.js","build":"vite build","preview":"vite preview","build:demo":"vite build --config vite.demo.config.js","prepublishOnly":"npm run build"},"_npmUser":{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"},"deprecated":"Renamed to retro-paddle: npm i retro-paddle. Same game, PongGame is now PaddleGame. Migration guide: https://www.npmjs.com/package/retro-paddle#migrating-from-retro-pong-game","repository":{"url":"git+https://bitbucket.org/chriscrossweb/pong-game-vite.git","type":"git"},"_npmVersion":"10.9.2","description":"A retro-style Pong game for the browser. You can drop it onto any web page in a easy way.","directories":{},"_nodeVersion":"22.14.0","dependencies":{"sound-manager-ts":"^5.5.7"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.0.5","playwright":"^1.60.0"},"_npmOperationalInternal":{"tmp":"tmp/retro-pong-game_5.1.1_1780422422294_0.2707664454981349","host":"s3://npm-registry-packages-npm-production"}},"5.2.1":{"name":"retro-pong-game","version":"5.2.1","keywords":["pong","pong game","retro-game","ping-pong","canvas","browser-game","game"],"author":{"url":"https://www.chriscreativecode.com","name":"Chris Schardijn","email":"chriscreativecode@gmail.com"},"license":"MIT","_id":"retro-pong-game@5.2.1","maintainers":[{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"}],"homepage":"https://pong.chriscreativecode.com/","bugs":{"url":"https://bitbucket.org/chriscrossweb/pong-game-vite/issues"},"dist":{"shasum":"e39ffbdf164bc6ac61d900116743c3b9d6b9034f","tarball":"https://registry.npmjs.org/retro-pong-game/-/retro-pong-game-5.2.1.tgz","fileCount":5,"integrity":"sha512-FiGXkl4bfiiMLk27R8jpbbPhtZiOVzqTLjGUhZKmbCi8mnbAMHWFORq7NA5cfh9nf/jG8ndJkJVtsbEGiGvMnw==","signatures":[{"sig":"MEUCIQDLBvofAQ57r6+oljLDUnWEowDU5WuyOH7CSCi0Hy5prAIgKIV0bAfgUgYN1AXQyBZ0y3iPyIfvi+1x8qsJ5J0dzWY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":993619},"main":"dist/pong-game.umd.js","type":"module","types":"dist/pong-game.d.ts","module":"dist/pong-game.es.js","exports":{".":{"types":"./dist/pong-game.d.ts","import":"./dist/pong-game.es.js","require":"./dist/pong-game.umd.js"}},"gitHead":"903858cc75a6a5666942075105efe1c0fc370ec2","scripts":{"dev":"vite --config vite.demo.config.js","build":"vite build && npm run build:demo","preview":"vite preview","build:demo":"vite build --config vite.demo.config.js","prepublishOnly":"npm run build"},"_npmUser":{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"},"deprecated":"Renamed to retro-paddle: npm i retro-paddle. Same game, PongGame is now PaddleGame. Migration guide: https://www.npmjs.com/package/retro-paddle#migrating-from-retro-pong-game","repository":{"url":"git+https://bitbucket.org/chriscrossweb/pong-game-vite.git","type":"git"},"_npmVersion":"10.9.2","description":"A retro-style Pong game for the browser. You can drop it onto any web page in a easy way.","directories":{},"_nodeVersion":"22.14.0","dependencies":{"sound-manager-ts":"^5.5.7"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.0.5","playwright":"^1.60.0"},"_npmOperationalInternal":{"tmp":"tmp/retro-pong-game_5.2.1_1781463682057_0.5171108048416242","host":"s3://npm-registry-packages-npm-production"}},"5.2.2":{"name":"retro-pong-game","version":"5.2.2","keywords":["pong","pong game","retro-game","ping-pong","canvas","browser-game","game"],"author":{"url":"https://www.chriscreativecode.com","name":"Chris Schardijn","email":"chriscreativecode@gmail.com"},"license":"MIT","_id":"retro-pong-game@5.2.2","maintainers":[{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"}],"homepage":"https://pong.chriscreativecode.com/","bugs":{"url":"https://bitbucket.org/chriscrossweb/pong-game-vite/issues"},"dist":{"shasum":"8fa11452805aca1334ea689da5ff62f8e74fb714","tarball":"https://registry.npmjs.org/retro-pong-game/-/retro-pong-game-5.2.2.tgz","fileCount":5,"integrity":"sha512-VAW9qVH0gnhmuwUJp+p3vCAppe4WpnrbCS+0H2rvpBT4rvJt84kBzBUWTKp1LpWzzZ936Gm+3iSexk4CmYo6Nw==","signatures":[{"sig":"MEUCIQDpCpof+auQOqyayWQ68YTTgL+v72fBidQImo/FMmlt0AIgHuyghCAWYZpFaAoZTPFAVDO5AyQ8o7BskpfYkVp+ATk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":995195},"main":"dist/pong-game.umd.js","type":"module","types":"dist/pong-game.d.ts","module":"dist/pong-game.es.js","exports":{".":{"types":"./dist/pong-game.d.ts","import":"./dist/pong-game.es.js","require":"./dist/pong-game.umd.js"}},"gitHead":"805f14976fa0356e6e1192921dae09d764dc6c82","scripts":{"dev":"vite --config vite.demo.config.js","build":"vite build && npm run build:demo","preview":"vite preview","build:demo":"vite build --config vite.demo.config.js","prepublishOnly":"npm run build"},"_npmUser":{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"},"deprecated":"Renamed to retro-paddle: npm i retro-paddle. Same game, PongGame is now PaddleGame. Migration guide: https://www.npmjs.com/package/retro-paddle#migrating-from-retro-pong-game","repository":{"url":"git+https://bitbucket.org/chriscrossweb/pong-game-vite.git","type":"git"},"_npmVersion":"10.9.2","description":"A retro-style Pong game for the browser. You can drop it onto any web page in a easy way.","directories":{},"_nodeVersion":"22.14.0","dependencies":{"sound-manager-ts":"^5.5.7"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.0.5","playwright":"^1.60.0"},"_npmOperationalInternal":{"tmp":"tmp/retro-pong-game_5.2.2_1782030584830_0.11352098755154683","host":"s3://npm-registry-packages-npm-production"}},"5.3.0":{"name":"retro-pong-game","version":"5.3.0","keywords":["pong","pong game","retro-game","ping-pong","canvas","browser-game","game"],"author":{"url":"https://www.chriscreativecode.com","name":"Chris Schardijn","email":"chriscreativecode@gmail.com"},"license":"MIT","_id":"retro-pong-game@5.3.0","maintainers":[{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"}],"homepage":"https://pong.chriscreativecode.com/","bugs":{"url":"https://bitbucket.org/chriscrossweb/retro-pong/issues"},"dist":{"shasum":"a522931bd60aa59e291c6db2c7bd94ad5caf33f8","tarball":"https://registry.npmjs.org/retro-pong-game/-/retro-pong-game-5.3.0.tgz","fileCount":5,"integrity":"sha512-rbK/zitGnZrIRf3DYYOjIJLWBBAL+rEuKGxHEduS/sQuzVxY2r7YMrq3JHl9KgCzlISpKrEv4AbLSjovoQej0A==","signatures":[{"sig":"MEUCIQDiyu7uvXD/JJWY7uPQsH5yCJsZPYOOaIWDxRZqBcMDXwIgNg0Jxn2eeS6LroFeKKrvba9sPd0P5k2thsYpQiAK+G0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1035351},"main":"dist/pong-game.umd.js","type":"module","types":"dist/pong-game.d.ts","module":"dist/pong-game.es.js","exports":{".":{"types":"./dist/pong-game.d.ts","import":"./dist/pong-game.es.js","require":"./dist/pong-game.umd.js"}},"gitHead":"b0ba320973b8f59f7fe70c56053a65d19e8f7599","scripts":{"dev":"vite --config vite.demo.config.js","build":"vite build && npm run build:demo","preview":"vite preview","build:demo":"vite build --config vite.demo.config.js","prepublishOnly":"npm run build"},"_npmUser":{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"},"deprecated":"Renamed to retro-paddle: npm i retro-paddle. Same game, PongGame is now PaddleGame. Migration guide: https://www.npmjs.com/package/retro-paddle#migrating-from-retro-pong-game","repository":{"url":"git+https://bitbucket.org/chriscrossweb/retro-pong.git","type":"git"},"_npmVersion":"10.9.2","description":"A retro-style Pong game for the browser. You can drop it onto any web page in a easy way.","directories":{},"_nodeVersion":"22.14.0","dependencies":{"sound-manager-ts":"^5.5.7"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.0.5","playwright":"^1.60.0"},"_npmOperationalInternal":{"tmp":"tmp/retro-pong-game_5.3.0_1782238052661_0.6916643861501235","host":"s3://npm-registry-packages-npm-production"}},"6.0.0":{"name":"retro-pong-game","version":"6.0.0","keywords":["pong","pong game","retro-game","ping-pong","canvas","browser-game","game"],"author":{"url":"https://www.chriscreativecode.com","name":"Chris Schardijn","email":"chriscreativecode@gmail.com"},"license":"MIT","_id":"retro-pong-game@6.0.0","maintainers":[{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"}],"homepage":"https://pong.chriscreativecode.com/","bugs":{"url":"https://bitbucket.org/chriscrossweb/retro-pong/issues"},"dist":{"shasum":"445f761e9ed8d1939dd5858e285988c45e69aed9","tarball":"https://registry.npmjs.org/retro-pong-game/-/retro-pong-game-6.0.0.tgz","fileCount":5,"integrity":"sha512-8X+UMNNJ3FY8Dx9N7ngU34Zlq3kaHJTPsxVIBVLkcMFMV9F0Pc/6XUGPSdCgGafqGqa8LfDn5WRu2yzlM6+IPQ==","signatures":[{"sig":"MEQCIHXkYKikMr97W2A+rc8RfbJ8zCEk8eYGS/+55QuKSepHAiBN+iJErQ0M+ZNVpymjRmiaBCMTnmBX7fzARwDL37k7HQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1349500},"main":"dist/pong-game.umd.js","type":"module","types":"dist/pong-game.d.ts","module":"dist/pong-game.es.js","exports":{".":{"types":"./dist/pong-game.d.ts","import":"./dist/pong-game.es.js","require":"./dist/pong-game.umd.js"}},"gitHead":"0e87c8999813ef973d74a9d9401a5f828a9629bb","scripts":{"dev":"vite --config vite.demo.config.js","test":"vitest run","build":"vite build && npm run build:demo","dev:app":"vite --config vite.app.config.js","preview":"vite preview","build:app":"vite build --config vite.app.config.js","dev:relay":"npm --prefix worker run dev","build:demo":"vite build --config vite.demo.config.js","build:site":"npm run build:app && npm run build:demo","dev:online":"vite --config vite.app.config.js --mode local-relay","test:watch":"vitest","dev:loopback":"vite --config vite.loopback.config.js","preview:site":"vite preview --config vite.app.config.js","prepublishOnly":"npm test && npm run build"},"_npmUser":{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"},"deprecated":"Renamed to retro-paddle: npm i retro-paddle. Same game, PongGame is now PaddleGame. Migration guide: https://www.npmjs.com/package/retro-paddle#migrating-from-retro-pong-game","repository":{"url":"git+https://bitbucket.org/chriscrossweb/retro-pong.git","type":"git"},"_npmVersion":"10.9.2","description":"A retro-style Pong game for the browser. You can drop it onto any web page in a easy way.","directories":{},"_nodeVersion":"22.14.0","dependencies":{"sound-manager-ts":"^5.9.0"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.0.5","vitest":"^3.2.7","playwright":"^1.60.0"},"_npmOperationalInternal":{"tmp":"tmp/retro-pong-game_6.0.0_1786816736125_0.7529028695162281","host":"s3://npm-registry-packages-npm-production"}},"6.1.0":{"name":"retro-pong-game","version":"6.1.0","keywords":["pong","pong game","retro-game","ping-pong","canvas","browser-game","game"],"author":{"url":"https://www.chriscreativecode.com","name":"Chris Schardijn","email":"chriscreativecode@gmail.com"},"license":"MIT","_id":"retro-pong-game@6.1.0","maintainers":[{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"}],"homepage":"https://pong.chriscreativecode.com/","bugs":{"url":"https://bitbucket.org/chriscrossweb/retro-pong/issues"},"dist":{"shasum":"0663dc0ac2ce61bd2301165c7a80a11d000e7912","tarball":"https://registry.npmjs.org/retro-pong-game/-/retro-pong-game-6.1.0.tgz","fileCount":5,"integrity":"sha512-GXU5tllITfWnf2Bq28l2cbjNS478wTa7ozg0he6vxmG8lJ/ojjgES7qIdyD9/I/tYXlz2CPTj5nw0nWa5rlkIQ==","signatures":[{"sig":"MEYCIQC2tcNmOIZx7O6OQRA7zKS2wk0HM3w70AqKLiJdyCVfHQIhAIXwIMsgr3IZIlfmw9bfDLj9C9uAs5PfJHlsS0jF0lnY","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1382746},"main":"dist/pong-game.umd.js","type":"module","types":"dist/pong-game.d.ts","module":"dist/pong-game.es.js","exports":{".":{"types":"./dist/pong-game.d.ts","import":"./dist/pong-game.es.js","require":"./dist/pong-game.umd.js"}},"gitHead":"2fbb9abaf28e41e00fa86cbe48862cdcf46aa745","scripts":{"dev":"vite --config vite.demo.config.js","test":"vitest run","build":"vite build && npm run build:demo","dev:app":"vite --config vite.app.config.js","preview":"vite preview","build:app":"vite build --config vite.app.config.js","dev:relay":"npm --prefix worker run dev","build:demo":"vite build --config vite.demo.config.js","build:site":"npm run build:app && npm run build:demo","dev:online":"vite --config vite.app.config.js --mode local-relay","test:watch":"vitest","dev:loopback":"vite --config vite.loopback.config.js","preview:site":"vite preview --config vite.app.config.js","prepublishOnly":"npm test && npm run build"},"_npmUser":{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"},"deprecated":"Renamed to retro-paddle: npm i retro-paddle. Same game, PongGame is now PaddleGame. Migration guide: https://www.npmjs.com/package/retro-paddle#migrating-from-retro-pong-game","repository":{"url":"git+https://bitbucket.org/chriscrossweb/retro-pong.git","type":"git"},"_npmVersion":"10.9.2","description":"A retro-style Pong game for the browser. You can drop it onto any web page in a easy way.","directories":{},"_nodeVersion":"22.14.0","dependencies":{"sound-manager-ts":"^5.9.0"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.0.5","vitest":"^3.2.7","playwright":"^1.60.0"},"_npmOperationalInternal":{"tmp":"tmp/retro-pong-game_6.1.0_1786878414292_0.27670832958051506","host":"s3://npm-registry-packages-npm-production"}},"6.2.0":{"name":"retro-pong-game","version":"6.2.0","keywords":["pong","pong game","retro-game","ping-pong","canvas","browser-game","game"],"author":{"url":"https://www.chriscreativecode.com","name":"Chris Schardijn","email":"chriscreativecode@gmail.com"},"license":"MIT","_id":"retro-pong-game@6.2.0","maintainers":[{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"}],"homepage":"https://pong.chriscreativecode.com/","bugs":{"url":"https://bitbucket.org/chriscrossweb/retro-pong/issues"},"dist":{"shasum":"4e6ca8508563c919ecd70af5beb04250e3d12f16","tarball":"https://registry.npmjs.org/retro-pong-game/-/retro-pong-game-6.2.0.tgz","fileCount":5,"integrity":"sha512-SDiMsjn6irMNQq1Ofye/wRSfzg3/vQLvKYrjqRGdDpTKR0iF8yv3WjVP4aqQL6Lg6tgAKVNMVYqLiS5ZK8UD7w==","signatures":[{"sig":"MEUCIQDL7tuETtz7R02CqItbrZWNtpFaMam3mff8wgoYbLBzlAIgOSnphBhKB9lj8hXMtjeVRM6ThxAmHREl5iZr9z2g/rc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1397609},"main":"dist/pong-game.umd.js","type":"module","types":"dist/pong-game.d.ts","module":"dist/pong-game.es.js","exports":{".":{"types":"./dist/pong-game.d.ts","import":"./dist/pong-game.es.js","require":"./dist/pong-game.umd.js"}},"gitHead":"0642d0f507bb2aca66603dd91f50891f892db58c","scripts":{"dev":"vite --config vite.demo.config.js","test":"vitest run","build":"vite build && npm run build:demo","dev:app":"vite --config vite.app.config.js","preview":"vite preview","build:app":"vite build --config vite.app.config.js","dev:relay":"npm --prefix worker run dev","build:demo":"vite build --config vite.demo.config.js","build:site":"npm run build:app && npm run build:demo","dev:online":"vite --config vite.app.config.js --mode local-relay","test:watch":"vitest","dev:loopback":"vite --config vite.loopback.config.js","preview:site":"vite preview --config vite.app.config.js","prepublishOnly":"npm test && npm run build"},"_npmUser":{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"},"deprecated":"Renamed to retro-paddle: npm i retro-paddle. Same game, PongGame is now PaddleGame. Migration guide: https://www.npmjs.com/package/retro-paddle#migrating-from-retro-pong-game","repository":{"url":"git+https://bitbucket.org/chriscrossweb/retro-pong.git","type":"git"},"_npmVersion":"10.9.2","description":"A retro-style Pong game for the browser. You can drop it onto any web page in a easy way.","directories":{},"_nodeVersion":"22.14.0","dependencies":{"sound-manager-ts":"^5.9.0"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.0.5","vitest":"^3.2.7","playwright":"^1.60.0"},"_npmOperationalInternal":{"tmp":"tmp/retro-pong-game_6.2.0_1786890826524_0.4467258687198401","host":"s3://npm-registry-packages-npm-production"}},"6.3.0":{"name":"retro-pong-game","version":"6.3.0","keywords":["pong","pong game","retro-game","ping-pong","canvas","browser-game","game"],"author":{"url":"https://www.chriscreativecode.com","name":"Chris Schardijn","email":"chriscreativecode@gmail.com"},"license":"MIT","_id":"retro-pong-game@6.3.0","maintainers":[{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"}],"homepage":"https://pong.chriscreativecode.com/","bugs":{"url":"https://bitbucket.org/chriscrossweb/retro-pong/issues"},"dist":{"shasum":"5ba68ab28d65f85a078446cee16a93d635f37db9","tarball":"https://registry.npmjs.org/retro-pong-game/-/retro-pong-game-6.3.0.tgz","fileCount":5,"integrity":"sha512-uXmRehUcJhc83XZLsGmEcmfsX6edbKcDPThln++3pZ6WhRtGv/Sb0SHPypyyxC7FH8//eEQLBD3lY887ZuyWAQ==","signatures":[{"sig":"MEUCIHn4a4bCSI8cYqyFJtkLr1GtIonZgociZXY/4v4LPK6NAiEAom/aSIAx/geV5yYVElhBewCGJir88HjWiLtocMqtt8c=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1423294},"main":"dist/pong-game.umd.js","type":"module","types":"dist/pong-game.d.ts","module":"dist/pong-game.es.js","exports":{".":{"types":"./dist/pong-game.d.ts","import":"./dist/pong-game.es.js","require":"./dist/pong-game.umd.js"}},"gitHead":"42630f4c5e0c2a0cfcdf49e1964c0dd9ab1afcbd","scripts":{"dev":"vite --config vite.demo.config.js","test":"vitest run","build":"vite build && npm run build:demo","dev:app":"vite --config vite.app.config.js","preview":"vite preview","build:app":"vite build --config vite.app.config.js","dev:relay":"npm --prefix worker run dev","build:demo":"vite build --config vite.demo.config.js","build:site":"npm run build:app && npm run build:demo","dev:online":"vite --config vite.app.config.js --mode local-relay","test:watch":"vitest","dev:loopback":"vite --config vite.loopback.config.js","preview:site":"vite preview --config vite.app.config.js","prepublishOnly":"npm test && npm run build"},"_npmUser":{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"},"deprecated":"Renamed to retro-paddle: npm i retro-paddle. Same game, PongGame is now PaddleGame. Migration guide: https://www.npmjs.com/package/retro-paddle#migrating-from-retro-pong-game","repository":{"url":"git+https://bitbucket.org/chriscrossweb/retro-pong.git","type":"git"},"_npmVersion":"10.9.2","description":"A retro-style Pong game for the browser. You can drop it onto any web page in a easy way.","directories":{},"_nodeVersion":"22.14.0","dependencies":{"sound-manager-ts":"^5.9.0"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.0.5","vitest":"^3.2.7","playwright":"^1.60.0"},"_npmOperationalInternal":{"tmp":"tmp/retro-pong-game_6.3.0_1786906403732_0.7122268521800166","host":"s3://npm-registry-packages-npm-production"}},"6.3.1":{"name":"retro-pong-game","version":"6.3.1","keywords":["pong","pong game","retro-game","ping-pong","canvas","browser-game","game"],"author":{"url":"https://www.chriscreativecode.com","name":"Chris Schardijn","email":"chriscreativecode@gmail.com"},"license":"MIT","_id":"retro-pong-game@6.3.1","maintainers":[{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"}],"homepage":"https://pong.chriscreativecode.com/","bugs":{"url":"https://bitbucket.org/chriscrossweb/retro-pong/issues"},"dist":{"shasum":"594c344cca7f0f8b77f7e4605c62c486157c3e38","tarball":"https://registry.npmjs.org/retro-pong-game/-/retro-pong-game-6.3.1.tgz","fileCount":5,"integrity":"sha512-KUyDRfI4zid6S1bvV5XPtfqqFtTpiljwVPhuJHwQkhJZDWS79kuYUR9qkhddd+iGKyo/BESKMjT+iHVS68f03A==","signatures":[{"sig":"MEYCIQDgjDxgJwY2G2kV3vPARddeRq/itZC1YzUsHb78uHs0NwIhAJR+vVB5iq4gZLAp1V4S0OeJfbE07BWlI0sr8gNv+NGq","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":2023689},"main":"dist/pong-game.umd.js","type":"module","types":"dist/pong-game.d.ts","module":"dist/pong-game.es.js","exports":{".":{"types":"./dist/pong-game.d.ts","import":"./dist/pong-game.es.js","require":"./dist/pong-game.umd.js"}},"gitHead":"44e7ce3ed946223e7c1e1a84d0a9039bbdb05046","scripts":{"dev":"vite --config vite.demo.config.js","test":"vitest run","build":"vite build && npm run build:demo","dev:app":"vite --config vite.app.config.js","preview":"vite preview","build:app":"vite build --config vite.app.config.js","dev:relay":"npm --prefix worker run dev","build:demo":"vite build --config vite.demo.config.js","build:site":"npm run build:app && npm run build:demo","dev:online":"vite --config vite.app.config.js --mode local-relay","test:watch":"vitest","dev:loopback":"vite --config vite.loopback.config.js","preview:site":"vite preview --config vite.app.config.js","prepublishOnly":"npm test && npm run build"},"_npmUser":{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"},"deprecated":"Renamed to retro-paddle: npm i retro-paddle. Same game, PongGame is now PaddleGame. Migration guide: https://www.npmjs.com/package/retro-paddle#migrating-from-retro-pong-game","repository":{"url":"git+https://bitbucket.org/chriscrossweb/retro-pong.git","type":"git"},"_npmVersion":"10.9.2","description":"A retro-style Pong game for the browser. You can drop it onto any web page in a easy way.","directories":{},"_nodeVersion":"22.14.0","dependencies":{"sound-manager-ts":"^5.9.0"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.0.5","vitest":"^3.2.7","playwright":"^1.60.0"},"_npmOperationalInternal":{"tmp":"tmp/retro-pong-game_6.3.1_1787164570065_0.6164140705693057","host":"s3://npm-registry-packages-npm-production"}},"6.9.0":{"name":"retro-pong-game","version":"6.9.0","keywords":["pong","pong game","retro-game","ping-pong","canvas","browser-game","game"],"author":{"url":"https://www.chriscreativecode.com","name":"Chris Schardijn","email":"chriscreativecode@gmail.com"},"license":"MIT","_id":"retro-pong-game@6.9.0","maintainers":[{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"}],"homepage":"https://pong.chriscreativecode.com/demo/","bugs":{"url":"https://bitbucket.org/chriscrossweb/retro-pong/issues"},"dist":{"shasum":"94f532a78f233a4e607679fd0962443f8c0f38f7","tarball":"https://registry.npmjs.org/retro-pong-game/-/retro-pong-game-6.9.0.tgz","fileCount":5,"integrity":"sha512-sVOwzR6NXnR5B7TJxQ+ETWsE3Oh72YXOaODey60Z5VF2u7n3iKnv1kS97YqzE5NxLdVg6EOwqLO9xfb2tOU9rw==","signatures":[{"sig":"MEUCIQDy1mfi5KgFSyGp37GLSO/pQY06j+gPbUqIea67lCr9/gIgOeNHP9/MSWWuSeM6K/emBIKPRnR9n3jCXmU2E4tVkmo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIQCN6KRdbKD74ka1LHRRfkpajODub59HIVNHp+mdoeRjOQIgFsC4vZkliY37B3ulbZEh6RpRUCUTkx8+dfFdBmJvuII=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":2102805},"main":"dist/pong-game.umd.js","type":"module","types":"dist/pong-game.d.ts","module":"dist/pong-game.es.js","exports":{".":{"types":"./dist/pong-game.d.ts","import":"./dist/pong-game.es.js","require":"./dist/pong-game.umd.js"}},"gitHead":"9309905ff3e56583e915a2cb8a87972f7730de5b","scripts":{"dev":"vite --config vite.demo.config.js","test":"vitest run","build":"vite build && npm run build:demo","dev:app":"vite --config vite.app.config.js","preview":"vite preview","build:app":"vite build --config vite.app.config.js","dev:relay":"npm --prefix worker run dev","build:demo":"vite build --config vite.demo.config.js","build:site":"npm run build:app && npm run build:demo","dev:online":"vite --config vite.app.config.js --mode local-relay","test:watch":"vitest","dev:loopback":"vite --config vite.loopback.config.js","preview:site":"vite preview --config vite.app.config.js","prepublishOnly":"npm test && npm run build"},"_npmUser":{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"},"deprecated":"Renamed to retro-paddle: npm i retro-paddle. Same game, PongGame is now PaddleGame. Migration guide: https://www.npmjs.com/package/retro-paddle#migrating-from-retro-pong-game","repository":{"url":"git+https://bitbucket.org/chriscrossweb/retro-pong.git","type":"git"},"_npmVersion":"10.9.2","description":"A retro-style Pong game for the browser. You can drop it onto any web page in a easy way.","directories":{},"_nodeVersion":"22.14.0","dependencies":{"sound-manager-ts":"^5.9.0"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.0.5","vitest":"^3.2.7","playwright":"^1.60.0"},"_npmOperationalInternal":{"tmp":"tmp/retro-pong-game_6.9.0_1787477866752_0.1917073072588018","host":"s3://npm-registry-packages-npm-production"}},"6.10.0":{"name":"retro-pong-game","version":"6.10.0","keywords":["pong","pong game","retro-game","ping-pong","canvas","browser-game","game"],"author":{"url":"https://www.chriscreativecode.com","name":"Chris Schardijn","email":"chriscreativecode@gmail.com"},"license":"MIT","_id":"retro-pong-game@6.10.0","maintainers":[{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"}],"homepage":"https://pong.chriscreativecode.com/demo/","bugs":{"url":"https://bitbucket.org/chriscrossweb/retro-pong/issues"},"dist":{"shasum":"cf4eb683a87c61a3e39b074c6c49ce09261a4e84","tarball":"https://registry.npmjs.org/retro-pong-game/-/retro-pong-game-6.10.0.tgz","fileCount":5,"integrity":"sha512-EhaGptGibiyusKVGJ9z7D2GUz3ACRnviE4Wap255fKVPUAAsqfIb0+7WkDvSpPv+o/M0bcomQdDGPrBVe+MN8w==","signatures":[{"sig":"MEUCIGqaFUYdB2SAv3q+rZDHGeDFprWBNwaTyZ6j8jpPyZO1AiEAyuChH95VAErb9Bq5VAghef+u3rbKpvrfldis1r3HD6I=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIAMr10Iq3dRVJ7RdNEKaJJlWbRbo6fnzl37IQMDPv90/AiEAhDmtHwXS8FYDTMOWK/xcXErHKQ4dCfRROi49fFFHBRo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":2121169},"main":"dist/pong-game.umd.js","type":"module","types":"dist/pong-game.d.ts","module":"dist/pong-game.es.js","exports":{".":{"types":"./dist/pong-game.d.ts","import":"./dist/pong-game.es.js","require":"./dist/pong-game.umd.js"}},"gitHead":"f958facb022e3639adbbd1f98b0710f3ab94aada","scripts":{"dev":"vite --config vite.demo.config.js","test":"vitest run","build":"vite build && npm run build:demo","dev:app":"vite --config vite.app.config.js","preview":"vite preview","build:app":"vite build --config vite.app.config.js","dev:relay":"npm --prefix worker run dev","build:demo":"vite build --config vite.demo.config.js","build:site":"npm run build:app && npm run build:demo","dev:online":"vite --config vite.app.config.js --mode local-relay","test:watch":"vitest","dev:loopback":"vite --config vite.loopback.config.js","preview:site":"vite preview --config vite.app.config.js","prepublishOnly":"npm test && npm run build"},"_npmUser":{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"},"deprecated":"Renamed to retro-paddle: npm i retro-paddle. Same game, PongGame is now PaddleGame. Migration guide: https://www.npmjs.com/package/retro-paddle#migrating-from-retro-pong-game","repository":{"url":"git+https://bitbucket.org/chriscrossweb/retro-pong.git","type":"git"},"_npmVersion":"10.9.2","description":"A retro-style Pong game for the browser. You can drop it onto any web page in a easy way.","directories":{},"_nodeVersion":"22.14.0","dependencies":{"sound-manager-ts":"^5.9.0"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.0.5","vitest":"^3.2.7","playwright":"^1.60.0"},"_npmOperationalInternal":{"tmp":"tmp/retro-pong-game_6.10.0_1787599213186_0.8243680941778062","host":"s3://npm-registry-packages-npm-production"}},"6.11.0":{"name":"retro-pong-game","version":"6.11.0","keywords":["pong","pong game","retro-game","ping-pong","canvas","browser-game","game"],"author":{"url":"https://www.chriscreativecode.com","name":"Chris Schardijn","email":"chriscreativecode@gmail.com"},"license":"MIT","_id":"retro-pong-game@6.11.0","maintainers":[{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"}],"homepage":"https://retro-paddle.chriscreativecode.com/demo/","bugs":{"url":"https://bitbucket.org/chriscrossweb/retro-pong/issues"},"dist":{"shasum":"27bab62ebb9f22a48129c384a717f73465f313a7","tarball":"https://registry.npmjs.org/retro-pong-game/-/retro-pong-game-6.11.0.tgz","fileCount":5,"integrity":"sha512-QE2AUZSuWJCUjd3Y3aoDue7WdtE1s9diYDUDaJsYAuS0gZl8X8aHgrPHMiyFHTfaoUOWo3OyEia7gvACLycOqQ==","signatures":[{"sig":"MEUCIQCQdR14rOqpPwr1U2oNUtpfpT2Ld9ZkP5wF0Pyz2XMTrwIgGdhl491YH3uRVW60cDn60W9xmuVp7RKdzfAf0uJOcTw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIQCzSJw617Ei8uYpP+WYk2qxN1kaR03DxY/qchqM5U9XwgIgcMt3oYhDBBRbt3uJhvGJYzw+BKqi1DzuJd/tnGFW0Aw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":2124004},"main":"dist/pong-game.umd.js","type":"module","types":"dist/pong-game.d.ts","module":"dist/pong-game.es.js","exports":{".":{"types":"./dist/pong-game.d.ts","import":"./dist/pong-game.es.js","require":"./dist/pong-game.umd.js"}},"scripts":{"dev":"vite --config vite.demo.config.js","test":"vitest run","build":"vite build && npm run build:demo","dev:app":"vite --config vite.app.config.js","preview":"vite preview","build:app":"vite build --config vite.app.config.js","dev:relay":"npm --prefix worker run dev","build:demo":"vite build --config vite.demo.config.js","build:site":"npm run build:app && npm run build:demo","dev:online":"vite --config vite.app.config.js --mode local-relay","test:watch":"vitest","dev:loopback":"vite --config vite.loopback.config.js","preview:site":"vite preview --config vite.app.config.js","prepublishOnly":"npm test && npm run build"},"_npmUser":{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"},"repository":{"url":"git+https://bitbucket.org/chriscrossweb/retro-pong.git","type":"git"},"_npmVersion":"10.9.2","description":"A retro-style Pong game for the browser. You can drop it onto any web page in a easy way.","directories":{},"_nodeVersion":"22.14.0","dependencies":{"sound-manager-ts":"^5.9.0"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.0.5","vitest":"^3.2.7","playwright":"^1.60.0"},"_npmOperationalInternal":{"tmp":"tmp/retro-pong-game_6.11.0_1788623392898_0.72870172215177","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Renamed to retro-paddle: npm i retro-paddle. Same game, PongGame is now PaddleGame. Migration guide:  https://www.npmjs.com/package/retro-paddle#migrating-from-retro-pong-game"}},"time":{"created":"2026-05-20T12:06:24.197Z","modified":"2026-09-05T15:58:41.976Z","1.1.0":"2026-05-20T12:06:24.492Z","1.1.1":"2026-05-20T13:16:51.841Z","1.1.2":"2026-05-20T14:16:09.735Z","1.1.3":"2026-05-20T14:28:23.481Z","2.0.0":"2026-05-21T07:36:44.287Z","3.0.0":"2026-05-21T16:45:44.956Z","3.0.1":"2026-05-21T17:00:03.423Z","4.0.0":"2026-05-22T13:08:43.169Z","4.0.1":"2026-05-22T13:19:07.730Z","4.1.0":"2026-05-23T08:15:52.541Z","4.1.1":"2026-05-23T08:18:51.366Z","4.2.0":"2026-05-23T09:43:43.429Z","4.2.1":"2026-05-23T09:54:46.784Z","5.0.0":"2026-05-27T11:32:40.150Z","5.1.0":"2026-05-30T12:22:37.832Z","5.1.1":"2026-06-02T17:47:02.445Z","5.2.1":"2026-06-14T19:01:22.244Z","5.2.2":"2026-06-21T08:29:44.975Z","5.3.0":"2026-06-23T18:07:32.820Z","6.0.0":"2026-08-15T17:58:56.307Z","6.1.0":"2026-08-16T11:06:54.498Z","6.2.0":"2026-08-16T14:33:46.758Z","6.3.0":"2026-08-16T18:53:23.925Z","6.3.1":"2026-08-19T18:36:10.239Z","6.9.0":"2026-08-23T09:37:46.875Z","6.10.0":"2026-08-24T19:20:13.315Z","6.11.0":"2026-09-05T15:49:52.988Z"},"bugs":{"url":"https://bitbucket.org/chriscrossweb/retro-pong/issues"},"author":{"url":"https://www.chriscreativecode.com","name":"Chris Schardijn","email":"chriscreativecode@gmail.com"},"license":"MIT","homepage":"https://retro-paddle.chriscreativecode.com/demo/","keywords":["pong","pong game","retro-game","ping-pong","canvas","browser-game","game"],"repository":{"url":"git+https://bitbucket.org/chriscrossweb/retro-pong.git","type":"git"},"description":"A retro-style Pong game for the browser. You can drop it onto any web page in a easy way.","maintainers":[{"name":"chris-schardijn","email":"chriscreativecode@gmail.com"}],"readme":"# retro-pong-game\n\n[![npm version](https://img.shields.io/npm/v/retro-pong-game.svg)](https://www.npmjs.com/package/retro-pong-game)\n[![License: MIT](https://img.shields.io/npm/l/retro-pong-game.svg)](https://opensource.org/licenses/MIT)\n[![Minzipped size](https://img.shields.io/bundlephobia/minzip/retro-pong-game.svg)](https://bundlephobia.com/package/retro-pong-game)\n[![Total downloads](https://img.shields.io/npm/dt/retro-pong-game.svg)](https://www.npmjs.com/package/retro-pong-game)\n[![JavaScript](https://img.shields.io/badge/JavaScript-ES6%2B-yellow.svg)](https://www.npmjs.com/package/retro-pong-game)\n\nA retro-style Pong game that runs in any browser. Drop it onto your website with a single constructor call. No frameworks, no extra configuration required.\n\n**[Demo and setup](https://retro-paddle.chriscreativecode.com/demo/)** | **[Play the full game](https://retro-paddle.chriscreativecode.com/)**\n\n> ## This package is now called retro-paddle\n>\n> 6.11.0 is the last release under the name `retro-pong-game`. Everything after\n> it lives at **[retro-paddle](https://www.npmjs.com/package/retro-paddle)**.\n>\n> ```bash\n> npm uninstall retro-pong-game\n> npm install retro-paddle\n> ```\n>\n> The game is the same one. What changed are the names: `PongGame` is now\n> `PaddleGame` and `PongFieldLoader` is now `PaddleFieldLoader`. Both old names\n> stay exported as deprecated aliases, so an import you forget to update keeps\n> running. The full list of what moved is in the\n> [migration guide](https://www.npmjs.com/package/retro-paddle#migrating-from-retro-pong-game).\n>\n> Why: PONG is a live registered trademark of Atari Interactive, in the exact\n> class this package sits in, and Atari has opposed compound names built on it\n> before. Leaning the package name on it was an unnecessary risk for an MIT\n> project.\n>\n> This version keeps working and is not going anywhere. It only stops getting\n> new releases.\n\n![retro-pong-game, two fields side by side: one with a serve waiting on the paddle, one mid rally with power-ups, an obstacle and a mine on the field](https://retro-paddle.chriscreativecode.com/screenshot-retro-paddle-v6-4.png)\n\n- Animated background: a starfield, a nebula, and a neon grid that lights up where the ball hits, in any combination. Stars and grid are on out of the box\n- Every point opens with a serve you aim and let go yourself\n- Particle effects on paddle hits, and a paddle that bends and lights up where the ball came off it\n- Countdown timer with configurable duration, and a last ten seconds that count\n- Adaptive AI difficulty (auto-adjusts after scoring streaks)\n- Local 2-player multiplayer mode on one keyboard\n- Keyboard and touch: drag anywhere on the canvas and the paddle follows\n- Power-ups and power-downs that randomly appear in the field, one of which lets you shoot\n- Angled wall obstacles that deflect the ball in unexpected directions\n- Sea mines that explode on contact and award a point to the opposite player\n- An end of match sequence that plays out, with the story of the match under it\n- In-game settings panel (changes saved to `localStorage`)\n- Stereo-panned sound effects\n- Fully configurable colors, sizes, and speeds\n- Hooks for running one match on two screens, so a page with a server of its own can put two players together\n- Type hints included for editors and TypeScript projects (ships with `.d.ts` declarations)\n\n## How to play\n\nYou control the **bottom paddle** using the arrow keys (or `A` / `D`). The **top paddle** is the AI opponent in single-player mode, or a second human player when `gameMode` is set to `\"multiplayer\"`.\n\n### Multiplayer\n\nThe package ships with local multiplayer: two people on one keyboard, Player 1\non the bottom paddle using `A` and `D`, and Player 2 on the top paddle using\nthe arrow keys. Turn it on with `isMultiplayer: true` or by setting\n`gameMode` to `\"multiplayer\"`.\n\nOnline multiplayer, where you play someone who is not sitting next to you, runs\non [retro-paddle.chriscreativecode.com](https://retro-paddle.chriscreativecode.com/). It needs a\nserver to pass the match between two browsers, so it is not part of this package\nand installing it will not give you a networked game. What the package does ship\nis the machinery underneath it: everything one game needs to hand its match to\nanother one. Bring your own connection and you can build the same thing. See\n[Two games, one match](#two-games-one-match).\n\n**Goal:** score as many points as possible before the timer runs out. A point is scored when the ball passes the opponent's paddle and exits through their side of the field. The player with the most points when the timer reaches zero wins.\n\n**The serve:** whoever won the last point holds the ball on their paddle. Slide the paddle to aim and press `Space` to let go, or lift your finger on a touch screen. The angle comes from how fast the paddle was moving at that moment, so it is the one shot in the game you have complete control over. A bar under the words empties while you aim, and when it runs out the ball goes on its own after a second and a half. Pong lives on the rally, so a point never waits on someone who is not paying attention. The computer never waits.\n\n**Controls:**\n\n| Key | Action |\n| --- | --- |\n| `A` / `D` | Move paddle left / right (Player 1 in multiplayer) |\n| `←` / `→` Arrow keys | Move paddle left / right (Player 2 in multiplayer, or combined in single-player) |\n| `Space` | Serve, while the ball is waiting on your paddle. Shoot, while you are holding the gun power-up |\n| `P` | Pause / resume, once the ball is in play |\n| `B` | Cycle ball image |\n\n**Scoring streaks** affect the AI: if you score three times in a row the AI gets harder; if the AI scores three times in a row it gets easier, so the game stays competitive. Pull three clear points ahead and the AI puts in an extra effort as well.\n\n**Power-ups and power-downs** appear randomly in the field, one every three seconds or so, with room for two on the field at once. Steer the ball into them to activate the effect. Power-ups (bright solid circles) give you a temporary advantage. Power-downs (dark circles with a rotating dashed ring) make the game harder for a few seconds. See [Power-ups & power-downs](#power-ups--power-downs) for the full list.\n\nA glowing **brick wall obstacle** also appears periodically. When the ball hits it, it deflects at an angle determined by the wall's orientation, and the wall stays put so you can play off it again. See [Obstacles](#obstacles).\n\nA **sea mine** spawns randomly in the field and sits there silently. If the ball touches it, the mine explodes and a point is awarded to the opposite player, the one who did not hit the mine. After an explosion the ball restarts from the paddle of the player who earned the point, giving them the serve. Mines disappear automatically after 10 seconds if not triggered. See [Mines](#mines).\n\n## Installation\n\n```bash\nnpm install retro-pong-game\n```\n\n## Quick start\n\n### 1. Add a container element to your HTML\n\n```html\n<div id=\"game\"></div>\n```\n\nThe game appends a `<canvas>` inside this element automatically.\n\n### 2. Import and create the game\n\n**New to this package?** Start with `isAdmin: true` to configure everything visually first.\n\n> **Fun tip:** press `B` during any game to cycle the ball through some images. Press again to go to the next image; one more press after the last image clears it back to a plain ball.\n\n```js\nimport { PongGame } from 'retro-pong-game';\n\nconst game = new PongGame({ isAdmin: true }, '#game');\n```\n\nOpen the settings panel (gear icon), adjust colors, sizes, and speeds live, then click **Copy** at the bottom to export your config as JSON. Paste it into your constructor and remove `isAdmin`:\n\n```js\nconst game = new PongGame(\n  {\n    /* paste your copied config here */\n  },\n  '#game'\n);\n```\n\nIf you already know your config, pass it directly:\n\n```js\nconst game = new PongGame(\n  {\n    canvasWidth: 600,\n    canvasHeight: 400,\n    hasSound: true,\n    colors: {\n      background: '#1a1a2e',\n      paddle: '#e94560',\n      ball: '#e94560',\n    },\n  },\n  '#game'\n);\n```\n\n### 3. Clean up\n\nCall `destroy()` before removing the element from the page:\n\n```js\ngame.destroy();\n```\n\n## The field before the game\n\nAn empty container has no height, so anything waiting on the game shows a flat\nstrip where the field is supposed to be. That wait is real: every sound is\nbundled into the JS, which puts the file over half a megabyte, and a match\nplayed over a connection cannot draw a field until the other side has said how\nbig it is.\n\n`PongFieldLoader` fills that gap with the field itself, at the size the match\nwill use, with a ball going back and forth between two paddles:\n\n```js\nimport { PongFieldLoader, PongGame } from 'retro-pong-game';\n\nconst loader = new PongFieldLoader('#game', {\n  width: 900,\n  height: 700,\n  label: 'Loading',\n});\n\n// Later, once you know what you are playing\nconst game = new PongGame({ canvasWidth: 900, canvasHeight: 700 }, '#game');\n```\n\nThere is nothing to clean up. A game takes away any loader it finds in its own\ncontainer when it puts its canvas there, and the drawing stops by itself if the\ncanvas leaves the page. Call `loader.destroy()` yourself when the game never\nturns up after all, and `loader.setLabel('Waiting')` to change the word while it\nruns.\n\n`PongGame.showLoader('#game', options)` does the same thing, for a page that\nonly imported `PongGame`. The loader takes `width`, `height`, `label`, `colors`,\n`paddleWidth` and `paddleHeight`; leave them out and it draws a 900 by 700 field\nin the default colours.\n\n## CDN (no npm required)\n\n```html\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\" />\n  <title>Pong</title>\n</head>\n<body>\n  <div id=\"game\"></div>\n\n  <script src=\"https://unpkg.com/retro-pong-game/dist/pong-game.umd.js\"></script>\n  <script>\n    const game = new PongGame.PongGame(\n      { canvasWidth: 600, canvasHeight: 400 },\n      '#game'\n    );\n  </script>\n</body>\n</html>\n```\n\n> **Self-hosting:** all audio and image assets are bundled inline in the JS file. You only need to serve a single file: `pong-game.umd.js` (or `pong-game.es.js` for ES modules). CDN providers (unpkg, jsDelivr) work out of the box as well.\n\n## Framework examples\n\n### React\n\n```jsx\nimport { useEffect, useRef } from 'react';\nimport { PongGame } from 'retro-pong-game';\n\nexport default function PongWidget() {\n  const gameRef = useRef(null);\n\n  useEffect(() => {\n    gameRef.current = new PongGame(\n      { canvasWidth: 600, canvasHeight: 400 },\n      '#pong-container'\n    );\n\n    return () => {\n      gameRef.current?.destroy();\n    };\n  }, []);\n\n  return <div id=\"pong-container\" />;\n}\n```\n\n### Vue 3\n\n```vue\n<template>\n  <div id=\"pong-container\" />\n</template>\n\n<script setup>\nimport { onMounted, onBeforeUnmount } from 'vue';\nimport { PongGame } from 'retro-pong-game';\n\nlet game = null;\n\nonMounted(() => {\n  game = new PongGame({ canvasWidth: 600, canvasHeight: 400 }, '#pong-container');\n});\n\nonBeforeUnmount(() => {\n  game?.destroy();\n});\n</script>\n```\n\n### Vanilla HTML + ES modules\n\n```html\n<div id=\"game\"></div>\n\n<script type=\"module\">\n  import { PongGame } from '/node_modules/retro-pong-game/dist/pong-game.es.js';\n\n  const game = new PongGame({ canvasWidth: 600, canvasHeight: 400 }, '#game');\n</script>\n```\n\n### TypeScript / editor autocomplete\n\nThe package is written in JavaScript but ships with type declarations (`.d.ts`), so TypeScript projects and editors with IntelliSense get full autocomplete on every config option. Import `PongGameConfig` to get type checking on the config object:\n\n```ts\nimport { PongGame, PongGameConfig } from 'retro-pong-game';\n\nconst config: PongGameConfig = {\n  sizeMode: \"fixed\",\n  canvasWidth: 400,\n  canvasHeight: 500,\n  paddleWidth: 70,\n  paddleHeight: 10,\n  ballDiameter: 7,\n  ballSpeed: 3,\n  timer: {\n    duration: '1:30',\n    hasBackgroundCircle: true,\n    labelColor: '#ffffff',\n    labelFontSize: 12,\n    circleColor: '#62cb31',\n    circleLineWidth: 5,\n    circleBackgroundColor: '#333',\n  },\n  colors: {\n    paddle: '#4daae8',\n    ball: '#ff7a59',\n    background: '#222',\n    centerline: '#9e9c9c',\n    score: '#9e9c9c',\n  },\n  animatedBackground: {\n    starfield: true,\n    nebular: false,\n    grid: true,\n  },\n  particleBounce: true,\n  paddleImpact: true,\n  particleConfig: {\n    particlesCount: 20,\n  },\n  hasSound: true,\n  onSoundToggle: (enabled: boolean) => {\n    // enabled = true  → sound is ON\n    // enabled = false → sound is OFF (muted)\n    console.log('Sound toggled:', enabled);\n  },\n};\n\nconst game = new PongGame(config, '#game');\n```\n\n## Configuration\n\nAll options are optional. Omitted values fall back to built-in defaults.\n\n```js\nnew PongGame(config, selector)\n```\n\nThe config is deep-merged in this order (later layers win):\n\n1. Built-in defaults\n2. Config you pass to the constructor\n3. Settings the player saved through the in-game settings panel (`localStorage`)\n\n### `PongGameConfig`\n\n| Option | Type | Default | Description |\n|---|---|---|---|\n| `sizeMode` | `\"responsive\"` \\| `\"auto\"` \\| `\"fixed\"` | `\"responsive\"` | Controls how the canvas sizes itself. `\"responsive\"` fills the container and auto-resizes on container changes (uses `ResizeObserver`). `\"auto\"` fills the container once at startup then stays fixed. `\"fixed\"` uses the exact `canvasWidth` and `canvasHeight` values and never auto-resizes. |\n| `canvasWidth` | `number` | `900` | Canvas width in px (only used when `sizeMode` is `\"fixed\"`) |\n| `canvasHeight` | `number` | `700` | Canvas height in px (only used when `sizeMode` is `\"fixed\"`) |\n| `paddleWidth` | `number` | `84` | Paddle width in px |\n| `paddleHeight` | `number` | `7` | Paddle height in px |\n| `ballDiameter` | `number` | `8` | Ball diameter in px |\n| `ballSpeed` | `number` | `3` | Starting ball speed (px per frame). Overridden by `gameMode` presets, so with the default `gameMode: \"medium\"` a game actually starts at `5`. Only `gameMode: \"multiplayer\"` leaves this value alone |\n| `playerPaddleSpeed` | `number` | `4` | Player paddle speed while an arrow key is held (px per frame). Overridden by `gameMode` presets, so the default `gameMode: \"medium\"` actually gives `5` |\n| `computerPaddleSpeed` | `number` | `4` | Computer paddle speed while an arrow key is held (px per frame). Overridden by `gameMode` presets when both are set |\n| `gameMode` | `string` | `\"medium\"` | Game mode: `\"easy\"`, `\"medium\"`, `\"hard\"`, `\"extreme\"` for single-player vs AI, or `\"multiplayer\"` for local 2-player on one keyboard |\n| `player1Name` | `string` | `\"\"` | Name shown above the bottom paddle and used in high scores. Falls back to \"Player 1\" in multiplayer |\n| `player2Name` | `string` | `\"\"` | Name shown above the top paddle and used in multiplayer high scores. Falls back to \"Player 2\" |\n| `particleBounce` | `boolean` | `true` | Particle burst when the ball hits a paddle |\n| `paddleImpact` | `boolean` | `true` | Rings run out through the paddle from the spot the ball came off it, brighter the faster the ball arrived |\n| `powerUps` | `boolean` | `true` | Enable power-ups and power-downs that randomly appear in the field |\n| `obstacles` | `boolean` | `true` | Enable the angled wall obstacle that randomly appears in the field and deflects the ball |\n| `mines` | `boolean` | `true` | Enable sea mines that randomly appear in the field and explode on contact |\n| `isMultiplayer` | `boolean` | `false` | Enable local 2-player mode on one keyboard. When `true`, overrides `gameMode` to `\"multiplayer\"` and shows both player names |\n| `hasSound` | `boolean` | `true` | Enable sound effects |\n| `ballImage` | `string` | `\"\"` | URL of a custom image to display on the ball from game start. Clipped to a circle and rotated with the ball. Pressing `B` still cycles through built-in images, which are picked by number with `setBallImageIndex()` and have nothing to do with this URL. |\n| `showSettings` | `boolean` | `true` | Show the gear icon that opens the in-game settings panel. Set to `false` to lock the settings from players |\n| `isAdmin` | `boolean` | `false` | Unlock difficulty and timing controls in the settings panel, plus a JSON export of the current config (see [Admin mode](#admin-mode)) |\n| `testPowerupsAndDowns` | `boolean` | `false` | Enable test mode that cycles through all power-ups and power-downs one by one (see [Test mode](#test-mode)) |\n| `useStoredSettings` | `boolean` | `true` | Read the settings saved by the in-game panel. Set it to `false` on a page with a settings screen of its own, so a block another page once saved cannot win from the config you pass here |\n| `simulate` | `boolean` | `true` | Whether this game decides what happens. `false` turns off the physics, the collisions and the scoring, and the match comes in through `applyState()` instead (see [Two games, one match](#two-games-one-match)) |\n| `localPlayer` | `0` \\| `1` | `0` | Which paddle this game's own input drives. `0` is the bottom one |\n| `sharedDevice` | `boolean` | same as `isMultiplayer` | Whether two people are sitting at this screen. It decides what a finger on the field steers: shared, the field splits down the middle; on your own, every touch steers the paddle named by `localPlayer`. Set it to `false` for a two player match played over a connection |\n| `autoRestart` | `boolean` | `true` | Whether pressing play again starts a new match straight away. `false` makes it a request, so two people can agree first |\n| `keepRunningWhenHidden` | `boolean` | `false` | Whether the match carries on while the tab is in the background. A browser stops handing out frames to a window nobody is looking at, which is right for a game played alone. Turn it on for a match against someone else, where the other side is watching a field that would otherwise freeze |\n| `onSoundToggle` | `(enabled: boolean) => void` | - | Called when the player toggles the sound icon |\n| `timer` | [`TimerConfig`](#timerconfig) | see below | Countdown timer settings |\n| `colors` | [`ColorConfig`](#colorconfig) | see below | Colors for game elements |\n| `animatedBackground` | [`AnimatedBackgroundConfig`](#animatedbackgroundconfig) | see below | Background animation |\n| `particleConfig` | [`ParticleConfig`](#particleconfig) | see below | Particle effect settings |\n| `wall` | [`WallConfig`](#wallconfig) | see below | The two side walls the ball bounces off |\n\n### `TimerConfig`\n\n| Option | Type | Default | Description |\n|---|---|---|---|\n| `duration` | `string` | `\"5:00\"` | Match duration in `\"M:SS\"` format |\n| `hasBackgroundCircle` | `boolean` | `true` | Show a circular progress ring around the timer |\n| `labelColor` | `string` | `\"#ffffff\"` | Timer text color |\n| `labelFontSize` | `number` | `12` | Timer text size in px |\n| `circleColor` | `string` | `\"#62cb31\"` | Countdown ring stroke color |\n| `circleLineWidth` | `number` | `5` | Countdown ring line width in px |\n| `circleBackgroundColor` | `string` | `\"#333\"` | Countdown ring background track color |\n\n### `ColorConfig`\n\n| Option | Type | Default | Description |\n|---|---|---|---|\n| `paddle` | `string` | `\"#62cb31\"` | Paddle color |\n| `ball` | `string` | `\"#e6e990\"` | Ball color |\n| `background` | `string` | `\"#000\"` | Canvas background color |\n| `centerline` | `string` | `\"#4fa185\"` | Center dividing line color |\n| `score` | `string` | `\"#62cb31\"` | Score text color |\n\n### `AnimatedBackgroundConfig`\n\n| Option | Type | Default | Description |\n|---|---|---|---|\n| `starfield` | `boolean` | `true` | Scrolling starfield effect |\n| `nebular` | `boolean` | `false` | Scrolling nebular cloud effect |\n| `grid` | `boolean` | `true` | Neon grid that lights up where the ball hits. See below |\n| `animateDashedLine` | `boolean` | `true` | Animate the dashed center line. When `true` the dashes scroll across the screen; `false` draws a static dashed line |\n\n> Any of them can run together. Clouds over stars is a matter of taste; the grid sits underneath either of the others without arguing with them, because it lives in the corners of the field rather than in the middle of it.\n\n#### The neon grid\n\nThe other two suggest depth behind a flat game and run on their own clock whatever the match is doing. The grid is the opposite: it lies in the same plane as the paddles and does almost nothing until the ball hits something. Then it carries that impact outward as a ring of light, relighting the squares the ring passes through.\n\nThat is the whole idea. Every return you make is answered by the field itself, so the background stops being scenery and becomes something the match writes on. A wall bounce is a small ring, a paddle return a bigger one, a mine the biggest, and the end of a point sends one out from the middle.\n\nIt takes its colour from `colors.paddle`, live, so it follows the theme and follows the colour picker while you are still dragging it.\n\n### `ParticleConfig`\n\n| Option | Type | Default | Description |\n|---|---|---|---|\n| `particlesCount` | `number` | `20` | Number of particles spawned per paddle hit |\n\n### `WallConfig`\n\n| Option | Type | Default | Description |\n|---|---|---|---|\n| `offset` | `number` | `0` | How far the wall sits from the edge of the canvas, in px |\n| `width` | `number` | `4` | Wall thickness in px |\n\nThe two side walls are drawn in the paddle color. The ball bounces off the inner edge and throws off particles where it lands, and the paddles stop against them, so nothing can slide out of view behind a wall.\n\n## Methods\n\n```js\n// Control the user paddle programmatically (e.g. on-screen buttons, touch)\ngame.pressLeft();    // start moving paddle left\ngame.releaseLeft();  // stop  moving paddle left\ngame.pressRight();   // start moving paddle right\ngame.releaseRight(); // stop  moving paddle right\n\n// Enable or disable sound effects\ngame.toggleSound(true);\ngame.toggleSound(false);\n\n// Check if sound is currently enabled\ngame.hasSound(); // → boolean\n\n// Cycle the ball image (same as pressing B). It returns the slot it landed on,\n// counting from zero, or -1 for a plain ball with no picture on it.\ngame.cycleBallImage(); // -> number | null\n\n// Put a particular one on instead of stepping. A match on two screens hands the\n// number across so both sides land on the same ball; stepping on from wherever\n// each one happened to be would not. A number pointing at no picture is ignored.\n// These are slots in the built-in row, not the ballImage config option, which is\n// a URL of a picture you bring yourself.\ngame.setBallImageIndex(2);\ngame.ballImageIndex; // -> number, -1 for a plain ball\n\n// Pause or resume the game (same as pressing P)\ngame.togglePause();\n\n// Change the line under PAUSED. It reads \"press P to resume\" by default, which\n// is right as long as whoever is looking at it is also the one who can carry\n// on. Showing the same match on several screens breaks that, so say who\n// stopped it. Two lines, split with a newline; null puts the wording back.\ngame.setPauseNote('Chris paused the match\\nWaiting for them to carry on');\ngame.setPauseNote(null);\n\n// Fire a bullet from a paddle holding the gun power-up (same as pressing\n// Space when there is nothing to serve). Handy for a button of your own on a\n// page that gets played on a phone, since there is no space bar there.\ngame.fireGun();\n\n// Adjust AI difficulty manually\ngame.increaseDifficulty();\ngame.decreaseDifficulty();\n\n// End the match now, with a verdict of your own\ngame.endMatch('win');\n\n// Write a message across the canvas, or a line under the score\ngame.showFlashMessage('NICE ONE!', '#62cb31', 2000);\ngame.setStatusLabel('Waiting for the other player');\n\n// Steer either paddle, and hand the whole match to another game.\n// See \"Two games, one match\" below.\ngame.setInput(1, { left: true });\nconst snapshot = game.getState();\ngame.applyState(snapshot, { ignorePaddle: 1 });\n\n// Listen in on what the game is doing\nconst onFlash = (data) => console.log(data);\ngame.on(PongGame.FLASH, onFlash);\ngame.off(PongGame.FLASH, onFlash);\n\n// Resize the canvas when the container changes size\nwindow.addEventListener('resize', () => game.resizeGame());\n\n// Get the internal SoundHelper for advanced audio control\nconst soundHelper = game.getSoundHelper();\n\n// Tear down the game completely (always call before unmounting)\ngame.destroy();\n```\n\n### Paddle control example\n\nUse `pressLeft` / `releaseLeft` / `pressRight` / `releaseRight` to wire on-screen buttons for touch or mouse control:\n\n```js\nfunction wireButton(btn, pressFn, releaseFn) {\n  btn.addEventListener('pointerdown', (e) => {\n    e.preventDefault();\n    btn.setPointerCapture(e.pointerId);\n    pressFn();\n  });\n  btn.addEventListener('pointerup',     releaseFn);\n  btn.addEventListener('pointercancel', releaseFn);\n}\n\nconst game = new PongGame(config, '#my-pong');\n\nwireButton(document.getElementById('btn-left'),\n  () => game.pressLeft(),  () => game.releaseLeft());\nwireButton(document.getElementById('btn-right'),\n  () => game.pressRight(), () => game.releaseRight());\n```\n\n## Two games, one match\n\nThe game can also run on two screens at once, with one of them deciding what happens and the other showing it. That is how the online mode on [the play page](https://retro-paddle.chriscreativecode.com/) works, and the pieces it needs are part of the package. The network itself is not: there is no socket, no dependency and no protocol in here, so a game you drop on your own page carries none of it.\n\nThe idea is that one side runs the match as usual and the other turns its own simulation off:\n\n```js\n// The side running the match. Nothing special about it.\nconst host = new PongGame({ isMultiplayer: true }, '#pong-host');\n\n// The side watching. It draws, animates and steers one paddle, and\n// gets everything else handed to it.\nconst guest = new PongGame({\n  isMultiplayer: true,\n  simulate: false,   // no ball physics, no collisions, no scoring here\n  localPlayer: 1,    // but this paddle is still mine and answers instantly\n  autoRestart: false // play again asks rather than starts\n}, '#pong-guest');\n```\n\nThen you move the state across, by whatever means you like:\n\n```js\n// On the deciding side, as often as you want to. Fifteen times a second\n// is plenty, since a Pong ball travels in a straight line between bounces.\nsend(host.getState());\n\n// On the watching side. ignorePaddle keeps your own paddle where you put\n// it instead of dragging it back to where the other side last saw it,\n// which is always half a round trip old.\nguest.applyState(incoming, { ignorePaddle: 1 });\n```\n\nInput goes the other way. `setInput` routes a key press to a specific paddle, which is what `pressLeft` and friends do for player 1:\n\n```js\n// The watching side acts on its own key first, then tells the other side.\nguest.setInput(1, { left: true });\nsend({ input: { left: true, right: false } });\n\n// The deciding side puts that straight into the match.\nhost.setInput(1, message.input);\n```\n\nA finger is not a key, so a drag travels as `target`: the spot on the field the\npaddle is being pulled to, in canvas pixels, or `null` on letting go. It wins\nover the keys for as long as it is set, and both sides count in the same pixels\nbecause they agreed on the field size.\n\n```js\n// The watching side. PongGame.DRAG fires while a finger is on the canvas.\nguest.on(PongGame.DRAG, (target) => send({ input: { target } }));\n\n// The deciding side. Leave target out of a message to keep the last one.\nhost.setInput(1, message.input);\n```\n\n### The rest of the pieces\n\n| Member | What it is for |\n| --- | --- |\n| `PongGame.STEP` | Fires once per simulation step on a game with `simulate: false`, with the tick number. This is where a watching side moves the ball on between snapshots. |\n| `PongGame.FLASH`, `PongGame.LABEL`, `PongGame.PREDICTION` | The words on the canvas and the crystal ball line. A watching side never works these out for itself, so pass them along and call `showFlashMessage`, `setStatusLabel` or `showPredictionLine` on the other end. |\n| `PongGame.REMATCH` | Someone pressed play again. With `autoRestart: false` that is only a request, so both sides can agree before a new match starts. |\n| `PongGame.SCORES` | The high score list opened or closed, with `{ open }`. It covers the field and stops the loop, which is a local matter on one screen and is not on two: the other side plays on while somebody reads a list. Pause the match on it and pick play back up when it closes. |\n| `PongGame.SERVE_REQUEST` | The player on a watching side wants to send a ball that is being held on the deciding side. A watching game runs no physics, so it cannot let go of the ball itself: pass this on and call `setInput(index, { serve: true })` there. It only fires for their own serve. |\n| `PongGame.SHOOT_REQUEST` | The player on a watching side pulled the trigger while holding the gun. Same story as the serve: the bullet belongs in the match that is actually running, so pass this on and call `setInput(index, { shoot: true })` there. |\n| `PongGame.DRAG` | The player is dragging their own paddle with a finger, with the spot on the field they are pulling it to, or `null` on letting go. A drag has nothing to travel in otherwise, since `setInput` carries a pair of booleans: pass this on and call `setInput(index, { target })` on the deciding side. Leave it out and the paddle follows the finger on the watching screen alone, while the side that rules on the ball sees it standing still. |\n| `state.serving` | Part of the snapshot: `'user'`, `'top'` or `null`. It is what lets a watching side draw the serve prompt on the right half of the field and know whose key press counts. `servingPlayer` reads it back after `applyState()`. |\n| `endMatch(result)` | Ends the match early with a verdict you choose. An opponent who walks off forfeits, so the win goes to whoever is still there and not to whoever was ahead. |\n| `showMatchEnd(result)` | Closes the match on a watching side, without writing the result down. When the clock simply runs out both sides get there on their own, but a match that is cut short only ends where it was cut, and the other screens sit on a match that no longer exists. The score stays with the side that ran it. |\n| `playBounceSound({ kind, x, multiplier })` | The sound of a bounce that happened somewhere else. A watching side runs no collisions, so it makes no sound of its own and a whole rally goes by in silence while the sparks fly. Which of the two paddles counts as yours follows `localPlayer`, so this works it out again on arrival rather than taking it from the wire. |\n| `PongGame.SOUND`, `playSound(name, pan)` | A sound that is the same wherever you are sitting: a power-up picked up, a mine going off, a point on the board. Pass the payload along and play it by name. Paddles are not in here, because whose paddle it was depends on which side you are on. It fires whether or not sound is on locally, so the other side decides for itself. |\n| `powerUp.spawnAt(x, y, type)`, `obstacle.spawnAt(x, y, angle)`, `mine.spawnAt(x, y)` | Put an item exactly where the other side put it, instead of rolling for a new spot. |\n| `setEndAction(action)`, `PongGame.END_ACTION` | A button of your own on the end screen, between play again and the high scores. The end screen knows about those two and nothing else, and over a connection that is too little: an opponent who walks off leaves a screen with nothing to do but read a list of results, while the room they came from is one press away. Where that press goes is your business, so the button fires `PongGame.END_ACTION` and leaves the rest to you. It stays put across matches. |\n\nTwo things worth knowing before you build on this. Both sides must agree on the field size, so use `sizeMode: 'fixed'` with the same width and height and scale the canvas with css if you need it smaller; with `sizeMode: 'responsive'` each screen measures its own container and the two end up counting in different pixels. And `win` and `loss` are always written from player 1's side, the bottom paddle. On a screen with `localPlayer: 1` the end screen turns that around before showing it, so nobody is congratulated on a match they lost.\n\n## Controls\n\n### Keyboard\n\n**Single player:** both `A` / `D` and `←` / `→` arrow keys control the bottom paddle. The top paddle is the AI opponent.\n\n**Multiplayer (`gameMode: \"multiplayer\"`):**\n\n| Key | Player | Action |\n| --- | --- | --- |\n| `A` / `D` | Player 1 (bottom) | Move paddle left / right |\n| `←` / `→` Arrow keys | Player 2 (top) | Move paddle left / right |\n| `Space` | Server | Serve, while the ball is waiting on your paddle. Shoot, while you are holding the gun power-up |\n| `P` | Both | Pause / resume, once the ball is in play |\n| `B` | Both | Cycle ball image |\n\nIn single-player mode both sets of keys move the bottom paddle for convenience.\n\n### Touch\n\nDrag anywhere on the canvas and the paddle follows your finger sideways. There is no need to touch the paddle itself: it sits at the very edge of the field, so a thumb on it covers the one thing you are trying to watch.\n\nIn single player any part of the canvas works, because there is only one paddle you could mean. In multiplayer the field splits down the middle and each half steers the paddle on its own side, so two people can share a tablet. Both can drag at the same time.\n\nSplitting it only makes sense while two people really are sitting at the same screen. Over a connection there is one paddle that is yours and the other player is somewhere else, so set `sharedDevice: false` and every touch steers your own paddle wherever it lands. Without it, whoever plays the top paddle reaches for the bottom of their phone, where their hand is, and takes hold of their opponent's paddle instead of their own.\n\nThe serve works the same way: while the ball is waiting on your paddle, drag to aim and lift your finger to send it. The prompt on the canvas says which of the two it is asking for, because it remembers whether this player last used a key or a finger.\n\nThe paddle does not jump to your finger, it heads for it, at the same top speed the keyboard gives. That keeps touch and keyboard playing the same game, and it keeps the angle you get off the paddle honest, since the ball reads how fast the paddle was actually travelling when it hit.\n\nMouse input is deliberately left out. The end of match buttons, the sound prompt and the settings panel all live on the same canvas, and a paddle chasing the cursor turns every one of those into a fight.\n\n## Power-ups & power-downs\n\nA glowing item appears somewhere in the middle of the field every three seconds or so, and there is room for two of them at a time. They arrive one at a time, never two in the same frame, and never two of the same kind. An item nobody takes expires after 18 seconds, flickering through the last five as a warning.\n\nTwo things help you reach them. About one in three is placed on the path the ball is already travelling, far enough ahead that you can see it coming and steer around it if you would rather not have it. And once the ball is within about 110 px, the item creeps toward it, so a near miss is usually a hit. Between them, a rally that used to end without either player getting near an item now tends to hand out a few per point.\n\nThe three kinds of item start on a stagger, so a match opens with them arriving one after another rather than all at once.\n\n### How to tell them apart\n\n| Visual | Type |\n| --- | --- |\n| **Bright solid circle**: slow pulsing outer ring | Power-up: helps you |\n| **Dark circle with rotating dashed ring**: fast flickering | Power-down: hurts you |\n\nWatch the ring rather than the colour. A colour says what an item does, and the two ball speed items keep the colours that match their names, cyan for slow and hot orange for fast. Whether that is good news is another matter, and it is the ring that tells you.\n\nThe reason those two read backwards from their names is worth knowing. You collect an item by hitting the ball into it, so the ball is always travelling away from you when the effect starts. Anything that acts on the ball therefore reaches your opponent first: slowing it gives them more time to get to it, and speeding it up gives them less.\n\n### Power-ups (beneficial)\n\n| Icon | Color | Effect | Duration |\n| --- | --- | --- | --- |\n| Animated paddle (wide) | Green | The paddle of the player who hits it becomes twice as wide. If you hit it, your paddle grows; if the opponent hits it, their paddle grows. A bar on the paddle counts the seconds down. Send the ball into a mine and you lose it there and then. | 12 s |\n| Pistol firing a round | Steel grey | Whoever hits the ball into it can shoot. `Space` sends a bullet out of the middle of their paddle, five a second at most, and a bar on the paddle counts the ten seconds down. A mine that is shot blows up without a point going anywhere. A power-up or power-down that is shot breaks, which is the way to stop your opponent reaching the one they were after. A brick wall that is shot falls apart, which is otherwise impossible before its ten seconds are up. A bullet that lands on their paddle throws sparks and does nothing else, so shooting never scores: it clears the field, and that is all. The ball is the one thing bullets pass by. The computer uses it too when it picks one up. | 10 s |\n| Animated ball (fast) | Orange-red | Ball speed increases by 40%, up to a ceiling of 18 px per frame. You hit the ball into the item, so it is on its way to your opponent when the effect starts, and a faster ball leaves them less time to reach it. | 5 s |\n| Animated snowflake | Ice blue | The opponent's paddle freezes. Whoever hits the ball into the powerup freezes the other player: if you hit it, the computer/Player 2 freezes; if the opponent hits it, you freeze. A bar on the frozen paddle counts the six seconds down, so whoever is stuck behind it knows how long that lasts. Works both in single-player and multiplayer. | 6 s |\n| Animated paddle (fast) | Yellow | The paddle of the player who hits it moves faster, and gets half again as wide with it. How much faster depends on the difficulty: 2.5x on easy, 2x on medium, 1.8x on hard and extreme. If you hit it, you get the boost; if the opponent hits it, they get it. The speed goes back to normal after five seconds, and a bar on the paddle counts them down. The extra width stays until the point ends. | 5 s, width until the point ends |\n| Blue and red horseshoe magnet, pulling filings in | Purple | The paddle of the player who hits it attracts the ball when it is close, making returns easier. If you hit it, your paddle gets the magnetic pull; if the opponent hits it, they get it. | 6 s |\n| Fading ghost | Orange | The opponent's paddle starts phasing, which makes it much harder to track. It surfaces as a faint shape, dims to almost nothing, and then goes altogether for a second and a half at a time. Their name plate fades out with it, so while the paddle is gone there is nothing on the field pointing at where it went. It never stays gone twice in a row, so there is a look at it every so often and the rest is down to memory. Whoever hits the ball into it phases the other player's paddle. | 5 s |\n| Crystal ball on a stand with the ball's path bouncing around inside it | Emerald green | Shows a dashed magic prediction line from the opponent's paddle to yours, including wall and obstacle bounces. The line appears when the ball hits the opponent's paddle and disappears when the ball reaches yours. Helps you aim returns by showing exactly where the ball will land. | Ball travel time |\n| Two arrows trading places | Dark orange | Your opponent's controls are inverted: they press left and go right. Hit the ball into it and it lands on them, never on you, so returning the ball gets a good deal harder for them. It works on the computer too, which has no keys to turn round and is instead inverted where it moves its paddle: every step it works out is still worked out correctly, and then it goes the other way. | 4 s |\n| One ball breaking into two | Magenta | The ball comes apart into two halves, both at the same speed and both still heading away from you. Your opponent gets to send exactly one of them back: the first ball their paddle meets goes back the normal way, and the next one bursts on contact. Every ball that gets past them is a point of its own, so the rally carries on after the first one and there are two to be had. Both halves carry the shield while it lasts, so mines, brick walls and power-downs leave them alone, and they can still collect the items that help. | Until the last ball is off the field |\n| Ball inside a hexagonal bubble, taking a hit | Mint green | A shield goes round the ball. Hit a mine and the mine explodes, but the ball carries straight on and the point that normally goes to your opponent never happens. Hit a brick wall and that explodes too, with the ball passing through instead of deflecting off the angle. Run into a power-down and it goes the same way, so nothing harmful on the field can reach you. The bubble holds two of those and shatters on the second. It only covers the ball on its way away from you: on the trip back it dims to a faint dashed outline and stops nothing. | 2 hits, or until the point ends |\n\n### Power-downs (detrimental)\n\n| Icon | Color | Effect | Duration |\n| --- | --- | --- | --- |\n| Animated paddle (narrow) | Red | The paddle of the player who hits it shrinks to half its normal width. If you hit it, your paddle shrinks; if the opponent hits it, their paddle shrinks. | 5 s |\n| Animated ball (slow) | Cyan | Ball speed is reduced by ~55%. You hit the ball into the item, so it is heading for your opponent when the effect starts, and a slower ball gives them more time to get there. | 5 s |\n| Ball snapping back to a paddle, with a U-turn arrow | Stamp red | The ball is pulled back to the paddle of whoever hit it into the item and lies there waiting. The shot you had just played is the one that gets taken away: your opponent is off the hook, and you start the point over from your own paddle. From there it is an ordinary serve, with the same aiming and the same bar counting down until the ball leaves on its own. Split balls in the air burst with it, because a serve is one ball, which makes this the way out of a split that is about to cost you two points. The computer holds the ball for about half a second before sending it back, so you can see what happened. | Until the ball is served again |\n| Animated falling ball | Dark purple | The ball curves toward the goal line of whoever hit it into the item: down for the bottom player, up for the top one. It reaches their side sooner and steeper, and what they hit back sags on the way over. The pull turns the ball without speeding it up, and it never bends further than about 53 degrees, so the ball keeps travelling sideways while it falls. This is not the magnet in reverse: the magnet works only near the paddle and pulls sideways to help, this bends the whole flight toward the goal you are defending. | 5 s |\n\nAnything with a clock on it draws a bar along the paddle it belongs to, shrinking as the seconds go, so you never have to guess how much of it is left. Two effects at once means two bars.\n\nThe times above are how long an effect runs while the rally is still going. A point ends all of them at once, for both players, whoever scored: paddles go back to their normal size, the ball goes back to its normal speed, and anything sitting on either player is lifted. A point from a mine is the exception, because play carries straight on from there rather than starting over: everything running stays running, and only the wide paddle of whoever set the mine off is taken away.\n\nA flash message appears on screen when an effect activates (e.g. **\"OPPONENT FROZEN!\"** or **\"NARROW PADDLE!\"**) and fades out over the effect's duration so you always know what is active.\n\nPower-ups and power-downs can be disabled via the in-game settings panel (toggle **Power-ups**) or through the config option `powerUps: false`.\n\n## Obstacles\n\nEvery 8 seconds a small brick wall appears somewhere in the middle of the field at a random angle, glowing orange. When the ball hits it, it deflects at a physically correct angle just like bouncing off a real angled surface, and the wall stays where it is, so the same one can be played off several times. It lasts 10 seconds (flickering during the final 3 as a warning), then disappears and respawns at a new position and angle after another 8 seconds.\n\nThe obstacle gives both players something unexpected to navigate around and can send the ball into hard-to-reach corners, which keeps long rallies interesting.\n\nThere is one way past a wall rather than off it. A ball carrying the [shield](#power-ups--power-downs) blows the wall up and keeps its course, so no deflection happens at all. Both halves of a [split](#power-ups--power-downs) carry one, so the same goes for them.\n\nA wall can also be shot. The [gun](#power-ups--power-downs) breaks one apart from a distance, which is the only way to clear the middle of the field without touching it.\n\nObstacles can be disabled via the config option `obstacles: false`.\n\n## Mines\n\nEvery 8 seconds a sea mine appears somewhere in the middle of the field. It looks like a floating spiked metal ball with a glowing red center. The mine stays visible for 10 seconds (flickering during the final 3 seconds as a warning), then disappears on its own if no one hits it.\n\nWhen the ball touches the mine:\n\n- The mine explodes with an expanding orange shockwave and sparks\n- A point is awarded to the player whose paddle did **not** send the ball into the mine (the opposite player)\n- The ball restarts from the paddle of the player who earned the point, giving them the serve\n- Play continues immediately without a pause\n\nA ball carrying the [shield](#power-ups--power-downs) is the exception, and so is either half of a [split](#power-ups--power-downs), which carries one. The mine still explodes, but nobody scores and nobody has to serve again: the ball travels on exactly as it was.\n\nMines can be disabled via the config option `mines: false`.\n\n## Settings panel\n\nThe game includes a built-in settings panel, behind the gear icon in the corner of the canvas. It slides in over the field, so the match stays visible while you are changing it.\n\n![The settings panel open over a running match, with its sections listed down the side](https://retro-paddle.chriscreativecode.com/screenshot-retro-paddle-v6-4-settings.png)\n\nThe panel is split into sections that fold open and shut, and it remembers which ones you had open. Game mode and player names, difficulty, the three field items, sound, canvas size, colors, the animated backgrounds, the timer, the visual effects and the side walls each have their own. Sections that only make sense to whoever set the game up are behind [admin mode](#admin-mode); the rest is there for every player.\n\nAt the bottom sit **High Scores**, which opens the scores without ending the match, **Save**, and **Close**. The **reset** button in the header puts the settings back to the defaults, and leaves your high scores where they are.\n\nEverything you save is written to `localStorage` and comes back on the next visit. To clear it by hand, see [Upgrading from an older version](#upgrading-from-an-older-version) below: the settings live nested inside a single storage entry that also holds the high scores, so removing that whole entry takes the scores with it.\n\n## Admin mode\n\n`isAdmin: true` is the recommended starting point when you are setting up the game for the first time. It unlocks the full settings panel so you can configure everything visually before committing to a config object.\n\n```js\nconst game = new PongGame({ isAdmin: true }, '#game');\n```\n\n**Workflow:**\n\n1. Start the game with `isAdmin: true`\n2. Open the settings panel (gear icon). You will see all configurable fields including sizes, speeds, colors, timer, and difficulty\n3. Adjust the values and watch the changes live\n4. Scroll to the bottom of the panel and click **Copy** to copy the full config as JSON\n5. Paste the JSON into your constructor and remove `isAdmin`\n\nIn admin mode the settings panel exposes:\n\n- **Difficulty fields**: `ballDiameter`, `ballSpeed`, `playerPaddleSpeed`, `computerPaddleSpeed`, `paddleWidth`, `paddleHeight`\n- **Canvas dimensions**: `canvasWidth`, `canvasHeight`\n- **Power-up test mode**: the switch for [test mode](#test-mode), which walks the field through every power-up in turn. A player who turns that on by accident has no idea what is happening to their game, so it is kept on this side of the fence\n- **Config JSON export**: a read-only textarea at the bottom of the panel showing the full current config as JSON, with a **Copy** button\n\n**Leaving admin mode:** the panel has an **Exit admin mode** button in the footer. Closing the panel with the gear icon, the Close button, Escape or a swipe only closes the panel, so you can look at the field and come back to the same admin controls afterwards. The game is built from the config you passed in, so the page that switched admin mode on is the one that switches it off again. The button says so with a `pong-admin-exit` event on `document`:\n\n```js\ndocument.addEventListener('pong-admin-exit', () => {\n  game.destroy();\n  game = new PongGame(myConfig, '#game'); // same config, without isAdmin\n});\n```\n\nWithout `isAdmin`, the difficulty fields and canvas dimensions are hidden from players so they cannot alter the game balance or the canvas size. The timer duration is not gated. Every player can change the match length from the settings panel, with or without admin mode. Do not ship `isAdmin: true` to end users.\n\n## Test mode\n\n`testPowerupsAndDowns: true` enables a special development mode that cycles through all power-ups and power-downs one by one so you can visually verify each effect works correctly.\n\n```js\nconst game = new PongGame({ testPowerupsAndDowns: true }, '#game');\n```\n\nIt can also be switched on and off from the settings panel while a game is running, in [admin mode](#admin-mode). The walkthrough turns obstacles, mines, particles and sound off while it runs, and those four are put back before anything is saved, so a **Save** during the walkthrough does not quietly leave a player with a field that has no mines and no sound.\n\n**How it works:**\n\n1. The game starts with obstacles, mines, particles and sound disabled to keep the test clean\n2. A power-up or power-down is spawned at the center of the canvas\n3. The ball is served toward it automatically so you can see the effect activate\n4. A label at the top of the screen shows which item is being tested (e.g. `Testing: WIDE_PADDLE (2/13)`)\n5. After 8 seconds all effects are cleared and the next item in the queue spawns\n6. Once every item has been tested, the label reads `TEST COMPLETE: all 13 tested`\n\nSHIELD_BALL is the one step that needs company. Mines and obstacles are switched off for the rest of the walkthrough to keep the field clean, which for the shield would leave nothing to look at but the bubble, so both come back for that step only. A brick wall and then a mine are placed by hand on the line the ball is already travelling: the wall shows the ball carrying straight on where it would normally deflect, the mine shows an explosion that costs nobody a point, and since that is two hits, the bubble shatters at the end of it.\n\n**Controls during test mode:**\n\n| Key | Action |\n| --- | --- |\n| `Arrow keys` / `A` `D` | Move the bottom paddle (as usual) |\n| `T` | Toggle serve direction between Player 1 (bottom) and Computer (top) |\n\nPressing `T` changes which player serves the ball toward the power-up. This is useful for effects that depend on which player hits the item (for example, FREEZE_OPPONENT freezes the other player, so serving from the computer side freezes your paddle and lets you see the ice overlay).\n\n**Test queue order:**\n\n1. CRYSTAL_BALL (power-up, emerald green)\n2. WIDE_PADDLE (power-up, green)\n3. SLOW_BALL (power-down, cyan)\n4. FREEZE_OPPONENT (power-up, orange)\n5. FAST_PADDLE (power-up, yellow)\n6. MAGNET_PADDLE (power-up, purple)\n7. INVISOR_PADDLE (power-up, orange)\n8. SHIELD_BALL (power-up, mint green)\n9. SPLIT_BALL (power-up, magenta)\n10. NARROW_PADDLE (power-down, red)\n11. FAST_BALL (power-up, orange-red)\n12. GRAVITY_BALL (power-down, dark purple)\n13. INVERT_CONTROLS (power-up, dark orange)\n14. GUN_PADDLE (power-up, steel grey)\n15. RETURN_TO_SENDER (power-down, stamp red)\n\nThis mode is intended for development and QA only. Do not enable it in production.\n\n## Canvas sizing (`sizeMode`)\n\nThe `sizeMode` option replaces the old `autoSize` and `autoResize` flags. It accepts one of three values:\n\n| Value | Canvas at startup | Auto-resizes on container change | When to use |\n|---|---|---|---|\n| `\"responsive\"` | Fills container | Yes (ResizeObserver) | Default. Best for responsive layouts where the container may change size at any time (e.g. window resize, CSS flex/grid changes). |\n| `\"auto\"` | Fills container | No | Canvas fills the container once at startup, then keeps that size. Useful when the container has a fixed size and you want to avoid the overhead of a ResizeObserver. |\n| `\"fixed\"` | Uses `canvasWidth` / `canvasHeight` | No | Exact pixel dimensions. Use this when you need a specific canvas size regardless of the container. |\n\n### Examples\n\n**Responsive (recommended for responsive layouts:**\n```js\nconst game = new PongGame(\n  { sizeMode: 'responsive' },\n  '#game'\n);\n```\n\nThe container must have a CSS-defined size (width and height). For example, to fill the viewport:\n\n```css\n#game {\n  width: 100%;\n  height: 100vh;\n}\n```\n\n**Fixed canvas dimensions:**\n```js\nconst game = new PongGame(\n  { sizeMode: 'fixed', canvasWidth: 640, canvasHeight: 480 },\n  '#game'\n);\n```\n\n**Manual resize trigger (if you need custom resize logic):**\n```js\nwindow.addEventListener('resize', () => game.resizeGame());\n```\n\n### Migrating from `autoSize` / `autoResize`\n\nIf you're upgrading from a previous version that used `autoSize` and `autoResize`, the old options still work (they're mapped automatically). However, the recommended approach is to switch to `sizeMode`:\n\n- `autoSize: true, autoResize: true` → `sizeMode: \"responsive\"`\n- `autoSize: true, autoResize: false` → `sizeMode: \"auto\"`\n- `autoSize: false, autoResize: false` → `sizeMode: \"fixed\"`\n\n## Troubleshooting\n\n### Upgrading from an older version\n\nThis is handled automatically. Saved settings carry a schema version, and when that version is out of date the game discards the stale settings on its own and falls back to the current defaults on the next page load. High scores are never touched by this. You should not need to do anything by hand.\n\nComing from 5.3.0 or older, that is what happens on the first load after the upgrade: settings saved by the old version are dropped once, and your players start on the current defaults. It is also how the new match length and the new side walls reach people who already had settings saved.\n\nIf you still want to clear the saved settings manually, for example while testing, remove only the settings key, `pong-game-chris-settings-v2`, rather than the whole storage container. All of the game's data lives nested inside one localStorage entry, so the settings key is not its own row in DevTools. Run this in the browser console instead:\n\n```js\nconst key = 'chriscreativecode.com-retro-pong-game';\nconst data = JSON.parse(localStorage.getItem(key) || '{}');\ndelete data['pong-game-chris-settings-v2'];\nlocalStorage.setItem(key, JSON.stringify(data));\n```\n\nDo not use \"Clear site data\" or remove the whole `chriscreativecode.com-retro-pong-game` entry for this. That entry also holds the high score keys, so clearing it wipes every recorded score along with the settings.\n\n## Browser support\n\nAll modern browsers with HTML Canvas support (Chrome, Firefox, Safari, Edge). When importing the ES module build, a bundler (Vite, Webpack, esbuild, Parcel) is recommended for production use.\n\n## Working on the game itself\n\nThis README covers the package. If you have the repository checked out and want to know how the pieces around it fit together, the npm scripts, the relay that makes online matches work, and what happens between two browsers from the first click to the final score, that is written up in [DEVELOPMENT.md](DEVELOPMENT.md).\n\n## License\n\nMIT © [Chris Schardijn](https://chriscreativecode.com)\n\n\n## Changelog\n\n### Every version at a glance\n\nOne line each, newest first. The full story of every release is below.\n\n| Version | In short |\n| --- | --- |\n| 6.11.0 | Slow ball and fast ball reach both halves of a split, and their factor lands once instead of twice |\n| 6.10.0 | The picture on the ball can be read and set, a countdown fits over a running field, and the gun says which key fires it |\n| 6.9.0 | The high score list can sit over a match that is still running, with a way back instead of a way to start over |\n| 6.8.0 | A field with a rally on it for the time before the game is there, so nobody stares at an empty strip |\n| 6.7.0 | A power-down that sends the ball back to your own paddle, and a serve clock that both players can see |\n| 6.6.0 | The two paddle colours go by where the paddles are instead of who is watching, the pause screen can say who paused, and the end screen can count down |\n| 6.5.0 | A screen that only follows a match now hears it and sees it end, and the clock no longer closes a match that has already closed |\n| 6.4.0 | Type definitions only: three methods that were already there |\n| 6.3.1 | Messages are written from the reader's chair, and a paddle hands back every bit of width it borrowed |\n| 6.3.0 | A finger travels across a connection, a paddle answers between points, and the field only splits when two people share the screen |\n| 6.2.0 | A watching game knows whose serve it is, buttons on a scaled canvas are clickable where you see them, and the power-up table matches what the game does |\n| 6.1.0 | The paddle answers a short tap, the opponent gets a colour of its own, and everything written on the field is set in an arcade face |\n| 6.0.0 | You serve the ball yourself, touch controls, three new items, the neon grid, an end sequence per result, and themes that use their machine's own colours |\n| 5.5.0 | A match can run on two screens at once, with the hooks for networking you bring yourself |\n| 5.4.0 | Five minute matches, and saved settings that carry a schema version |\n| 5.3.0 | The crystal ball, and a test mode that walks through every item one by one |\n| 5.2.2 | A crash on damaged storage, and a listener that piled up on every restart |\n| 5.2.0 | High scores rebuilt, with a column per difficulty and a button to look at them mid-match |\n| 5.1.1 | Two power-ups that quietly did nothing when the computer got them |\n| 5.1.0 | Side walls, and difficulty as named presets instead of a formula |\n| 5.0.0 | Mines, angled obstacles, power-ups, and two players on one keyboard |\n| 4.2.0 | Your own image on the ball, and every asset bundled into the JS |\n| 4.1.0 | Lighter rendering and lighter audio |\n\n### Since 5.3.0, the short version\n\n5.4.0 and 5.5.0 were prepared in this repository but never went out to npm, so\nan upgrade from 5.3.0 brings all three releases at once. The headlines:\n\n- **You serve the ball yourself.** Hold it on the paddle, aim, and let go with `Space` or a lifted finger\n- **Touch controls.** Drag anywhere on the canvas. The game was unplayable on a phone before this\n- **Three new items**: the shield ball, the split ball, and a crystal ball line that reads the bounces ahead. Invert controls became a power-up and now works against the computer as well\n- **Power-ups are worth chasing.** They arrive three times as often, land on the ball's path, and drift toward a ball that comes close\n- **A third background**, the neon grid, which lights up where the ball hits instead of scrolling past behind it\n- **The end of a match plays out**, and puts the story of it on screen: top ball speed, items taken, biggest lead\n- **A match can run on two screens**, with one side deciding and the other showing. The networking is yours to bring; the hooks are in the package\n- **The settings panel was rebuilt** into sections that fold open, and the reset button no longer takes your high scores with it\n- **The default match is five minutes**, not three\n- Twenty-two bug fixes, from a bounce angle that jumped for no reason to a magnet that flung the ball away instead of pulling it in\n\nThe full list is below.\n\n### v6.11.0\n\n#### Slow ball and fast ball reach both halves of a split\n\nBoth items worked on the one ball the game calls the real one, and since the\nsplit ball arrived there can be two out there. Pick up slow ball, split the ball\na moment later, and the half that is not the real one is born at the slowed\nspeed and is still travelling at it when the five seconds are up. It crawls\nacross the field for the rest of the point. The same thing the other way round:\nsplit first, and only the real ball slows down, so the two halves of one split\nend up at different speeds.\n\nThe scaling and the restore now both run over every ball on the field, and the\nlist is read again at the moment the timer fires, so a ball that was split off\nhalfway through the five seconds gets its speed back as well.\n\n#### The factor landed twice\n\nSetting the tracker to the new speed already carries that speed to the ball, and\nthe ball's own horizontal and vertical speed were then multiplied by the same\nfactor on top of it. Slow ball went to a fifth of the speed instead of not quite\nhalf, and fast ball shot past the ceiling of 18 that is there to keep it\nreturnable. The factor is applied once now.\n\n### v6.10.0\n\n#### The picture on the ball is a number now\n\n`cycleBallImage()` stepped to the next picture and said nothing about where it\nhad got to, so there was no way to find out which ball was on screen and no way\nto put a particular one there. That is fine on one screen and useless on two:\nthe side that hears about the change has to arrive at the same picture, and\nstepping on from wherever it happened to be is not the same thing.\n\nSo there are three of them now, and they all count from zero with `-1` for a\nplain ball with no picture on it:\n\n```js\nconst picture = game.cycleBallImage();   // where it got to, or null\ngame.setBallImageIndex(picture);         // put that one on, no stepping\ngame.ballImageIndex;                     // what is on there right now\n```\n\n`setBallImageIndex()` checks what it is handed and ignores anything that points\nat no picture, which matters when the number crossed a wire to get there: a ball\nthat vanishes because a message was malformed is worse than a message that does\nnothing. Passing along the number rather than the fact that a key was pressed\nalso means two people pressing at the same moment end up looking at the same\nball instead of each stepping one further.\n\nThe word index is in those two names on purpose. The `ballImage` config option\nis a URL of a picture you bring yourself, and these are slots in the row the\npackage brings along: two different things, and one name for both would have\nthem read as each other. A picture of your own does not get a slot, so\n`ballImageIndex` stays `-1` while one is on the ball.\n\n#### A countdown that fits over a running field\n\n`setRematchNote({ count })` fills the screen with the number, and it only ever\ndid that over the end screen, because that is where the note is drawn. Between\ntwo matches that is exactly where you want it. Before the first one it is\nnowhere: a field that has not finished anything has no end screen to draw on,\nand the countdown was dropped without a word.\n\nIt now gets painted over a running field as well, so a match can count itself\nin whether or not the last one is still on screen. Two smaller things that were\nwrong for the same reason: a note that came in before the game had finished\nbuilding itself is kept instead of thrown away, and `restartGame()` clears it,\nso a number cannot sit over a match that has already started.\n\n#### The gun says which key fires it\n\nThe message read `GUN! SPACE TO SHOOT`, and on a touch screen `GUN! TAP TO\nSHOOT`. That second one names no target: there is nothing on a phone called tap.\nIt now reads `GUN! TAP SHOOT TO FIRE`, after the key that fires it, and\n`GUN! SPACE TO FIRE` on a keyboard.\n\n#### Whose paddle is whose\n\nWith `isMultiplayer: true` and `sharedDevice: false` there are two people on two\nscreens, and until now both name labels looked the same on both of them. The one\nbelonging to whoever is sitting in front of the screen is now drawn in that\npaddle's own colour and brighter than the other, so it takes a glance rather\nthan working it out. Nothing changes with two players on one keyboard or against\nthe computer, where both paddles are in the same room and there is nothing to\ntell apart.\n\n### v6.9.0\n\n#### The score list over a running match\n\n`showHighScores()` was built for the end of a match. It has been reachable\nmid-match for a while, and there it said two things that were not true.\n\nThe button along the bottom read \"Play Again\", so pressing it asked for a new\nmatch while the current one was still going. On one screen that threw away a\ngame somebody was playing; on two it asked the other side for a rematch out of\nnowhere. Over a running match that button now reads \"Back to the game\" and puts\nyou back where you were.\n\nThere is also a close cross in the top corner. Escape already closed the list,\nbut a phone has no Escape, so on a touch screen the list had no way out at all.\n\nA new event comes with","readmeFilename":"README.md"}