{"_id":"@der-lukas/magma-player","_rev":"8-b0683dbdb1f338b574514d0dd8e74e77","name":"@der-lukas/magma-player","dist-tags":{"latest":"1.2.1"},"versions":{"1.0.0":{"name":"@der-lukas/magma-player","version":"1.0.0","keywords":["video","alpha","mask","compositing","webgl","canvas","synchronization","transparency","mp4","video-player"],"author":{"name":"der-lukas"},"license":"MIT","_id":"@der-lukas/magma-player@1.0.0","maintainers":[{"name":"der-lukas","email":"admin@der-lukas.net"}],"homepage":"https://github.com/der-lukas/magma-player#readme","bugs":{"url":"https://github.com/der-lukas/magma-player/issues"},"dist":{"shasum":"0d858af339bcd21147bd82125e1a8638159be860","tarball":"https://registry.npmjs.org/@der-lukas/magma-player/-/magma-player-1.0.0.tgz","fileCount":10,"integrity":"sha512-QZoD/M6X5KtVIV8UOCAJvYM/r58umBxS+cgOg2Rc3z0d+sf9Bt9Zlbgg6NxXI+DUqdNdg6kCG+e4piWSYgBarw==","signatures":[{"sig":"MEQCIE8F7jt6HQ4sRwyejyHIMJrI6CSb0qCEnQRrnVr9CySvAiBU0kyyoW+eLqDIYjv+BkcLjO3Li8JxXamUWjFbQDeZqQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":206886},"main":"MagmaPlayer.js","types":"MagmaPlayer.d.ts","module":"MagmaPlayer.js","browser":"dist/magma-player.umd.js","engines":{"node":">=14.0.0"},"exports":{".":{"import":"./MagmaPlayer.js","browser":"./dist/magma-player.umd.js","require":"./dist/magma-player.cjs.js"},"./package.json":"./package.json"},"gitHead":"5fa6d0e688b8ef7255addfced7eb05430c35c418","scripts":{"build":"npm run build:cjs && npm run build:umd","build:cjs":"esbuild MagmaPlayer.js --bundle --format=cjs --outfile=dist/magma-player.cjs.js --platform=browser","build:umd":"esbuild MagmaPlayer.js --bundle --format=iife --global-name=MagmaPlayer --outfile=dist/magma-player.umd.js --platform=browser --minify","prepublishOnly":"npm run build"},"_npmUser":{"name":"der-lukas","email":"admin@der-lukas.net"},"repository":{"url":"git+https://github.com/der-lukas/magma-player.git","type":"git"},"_npmVersion":"11.5.1","description":"A reusable library for compositing color and alpha mask videos with perfect synchronization using a shared clock","directories":{},"_nodeVersion":"24.5.0","_hasShrinkwrap":false,"devDependencies":{"esbuild":"^0.27.0"},"_npmOperationalInternal":{"tmp":"tmp/magma-player_1.0.0_1764245214765_0.8708625690695766","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@der-lukas/magma-player","version":"1.0.1","keywords":["video","alpha","mask","compositing","webgl","canvas","synchronization","transparency","mp4","video-player"],"author":{"name":"der-lukas"},"license":"MIT","_id":"@der-lukas/magma-player@1.0.1","maintainers":[{"name":"der-lukas","email":"admin@der-lukas.net"}],"homepage":"https://github.com/der-lukas/magma-player#readme","bugs":{"url":"https://github.com/der-lukas/magma-player/issues"},"dist":{"shasum":"cee1d850ceace3a5dcd7c0dc0cde0fe32193805e","tarball":"https://registry.npmjs.org/@der-lukas/magma-player/-/magma-player-1.0.1.tgz","fileCount":17,"integrity":"sha512-FVbF+4H4fj8hAqPE6QIKdHmuVY6+Oj49LNqz7bE0sUlcSmAAM0fWlTD6e2Bu0m1KiZNnw8CIMyuZGWROyfF2LA==","signatures":[{"sig":"MEYCIQCDJky1j0lb0vALfUYcKLRfX4/7e0otMn09pjRVS0VsDQIhAPHOptwkRDjzHxxCzFRoeBh9LTOT/46aTNDGj4Z17mbP","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":232809},"main":"MagmaPlayer.js","types":"MagmaPlayer.d.ts","module":"MagmaPlayer.js","browser":"dist/magma-player.umd.js","engines":{"node":">=14.0.0"},"exports":{".":{"import":"./MagmaPlayer.js","browser":"./dist/magma-player.umd.js","require":"./dist/magma-player.cjs.js"},"./react":"./react/index.js","./angular":"./angular/index.ts","./vanilla":"./vanilla/index.js","./package.json":"./package.json"},"gitHead":"2da31e301feb9fcffff848f547d9d976b9263b36","scripts":{"build":"npm run build:cjs && npm run build:umd","build:cjs":"esbuild MagmaPlayer.js --bundle --format=cjs --outfile=dist/magma-player.cjs.js --platform=browser","build:umd":"esbuild MagmaPlayer.js --bundle --format=iife --global-name=MagmaPlayer --outfile=dist/magma-player.umd.js --platform=browser --minify","prepublishOnly":"npm run build"},"_npmUser":{"name":"der-lukas","email":"admin@der-lukas.net"},"repository":{"url":"git+https://github.com/der-lukas/magma-player.git","type":"git"},"_npmVersion":"11.5.1","description":"A reusable library for compositing color and alpha mask videos with perfect synchronization using a shared clock","directories":{},"_nodeVersion":"24.5.0","_hasShrinkwrap":false,"devDependencies":{"esbuild":"^0.27.0"},"_npmOperationalInternal":{"tmp":"tmp/magma-player_1.0.1_1764245486859_0.7599693206757381","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@der-lukas/magma-player","version":"1.1.0","keywords":["video","alpha","mask","compositing","webgl","canvas","synchronization","transparency","mp4","video-player"],"author":{"name":"der-lukas"},"license":"MIT","_id":"@der-lukas/magma-player@1.1.0","maintainers":[{"name":"der-lukas","email":"admin@der-lukas.net"}],"homepage":"https://github.com/der-lukas/magma-player#readme","bugs":{"url":"https://github.com/der-lukas/magma-player/issues"},"dist":{"shasum":"ce9f1cf4170dc295abdb91fd2788cc9e9b9241be","tarball":"https://registry.npmjs.org/@der-lukas/magma-player/-/magma-player-1.1.0.tgz","fileCount":22,"integrity":"sha512-ULtDXnWGvtUZ1JgPYQSWsS1ThelYWDmomJA9Gf7ZPtMWGktdpuIsFJi+tuyPAz3AtrtzU4ZQUHRf8TAu3CzOkw==","signatures":[{"sig":"MEUCIQCRCBJymSmG7ShEVRATA6DYY03rBUgMDviy/GMnlavJ5QIgTTFSxDUqCJ1YDvWt9QZS7P+IokkPF8OH6s+jFRvXSmU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":259719},"main":"MagmaPlayer.js","types":"MagmaPlayer.d.ts","module":"MagmaPlayer.js","browser":"dist/magma-player.umd.js","engines":{"node":">=14.0.0"},"exports":{".":{"import":"./MagmaPlayer.js","browser":"./dist/magma-player.umd.js","require":"./dist/magma-player.cjs.js"},"./react":"./react/index.js","./angular":{"types":"./angular/index.d.ts","import":"./angular/index.js"},"./vanilla":"./vanilla/index.js","./package.json":"./package.json"},"gitHead":"d3ae64ea809c15eccd5cbe5a75a2dc5ef70a27ae","scripts":{"build":"npm run build:react && npm run build:angular && npm run build:cjs && npm run build:umd","build:cjs":"esbuild MagmaPlayer.js --bundle --format=cjs --outfile=dist/magma-player.cjs.js --platform=browser","build:umd":"esbuild MagmaPlayer.js --bundle --format=iife --global-name=MagmaPlayer --outfile=dist/magma-player.umd.js --platform=browser --minify","build:react":"npm run build:react:js && rm -f react/@react__stub.d.ts","build:angular":"tsc --project tsconfig.angular.json && rm -f angular/@angular__core.d.ts","build:react:js":"esbuild react/MagmaPlayer.jsx --bundle --format=esm --outfile=react/MagmaPlayer.js --platform=browser --external:react --external:react-dom --external:../MagmaPlayer.js --jsx=automatic --packages=external","prepublishOnly":"npm run build"},"_npmUser":{"name":"der-lukas","email":"admin@der-lukas.net"},"repository":{"url":"git+https://github.com/der-lukas/magma-player.git","type":"git"},"_npmVersion":"11.5.1","description":"A reusable library for compositing color and alpha mask videos with perfect synchronization using a shared clock","directories":{},"_nodeVersion":"24.5.0","_hasShrinkwrap":false,"devDependencies":{"esbuild":"^0.27.0","typescript":"^5.9.3","@types/node":"^24.10.1"},"peerDependencies":{"react":">=16.8.0","@angular/core":">=17.0.0"},"_npmOperationalInternal":{"tmp":"tmp/magma-player_1.1.0_1764252341514_0.454124113112927","host":"s3://npm-registry-packages-npm-production"}},"1.1.1":{"name":"@der-lukas/magma-player","version":"1.1.1","keywords":["video","alpha","mask","compositing","webgl","canvas","synchronization","transparency","mp4","video-player"],"author":{"name":"der-lukas"},"license":"MIT","_id":"@der-lukas/magma-player@1.1.1","maintainers":[{"name":"der-lukas","email":"admin@der-lukas.net"}],"homepage":"https://github.com/der-lukas/magma-player#readme","bugs":{"url":"https://github.com/der-lukas/magma-player/issues"},"dist":{"shasum":"f645c2026a158c6ba32d2e1d10dc3014c64e6e73","tarball":"https://registry.npmjs.org/@der-lukas/magma-player/-/magma-player-1.1.1.tgz","fileCount":20,"integrity":"sha512-/SKDO3L1sPuOuOb9kA8pTs3jh6uTACUKc2/mFl1L5/dcL82XNQkw/ZysjJMbhEoSGrTCfyRVjFFhfWjhE0+7/g==","signatures":[{"sig":"MEYCIQDOxFSnsYkoNkwW2OLqWVE2BMas9UNHcC6z3G84SvcyrAIhALw6nE8U90uTtM0IsilG0LeBOGpNrrrR9dx2TQ2AIlen","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":245135},"main":"MagmaPlayer.js","types":"MagmaPlayer.d.ts","module":"MagmaPlayer.js","browser":"dist/magma-player.umd.js","engines":{"node":">=14.0.0"},"exports":{".":{"import":"./MagmaPlayer.js","browser":"./dist/magma-player.umd.js","require":"./dist/magma-player.cjs.js"},"./react":"./react/index.js","./angular":"./angular/index.ts","./vanilla":"./vanilla/index.js","./package.json":"./package.json"},"gitHead":"101a20be54d8506b640366fcd4f756bf96ca64cc","scripts":{"build":"npm run build:react && npm run build:angular && npm run build:cjs && npm run build:umd","build:cjs":"esbuild MagmaPlayer.js --bundle --format=cjs --outfile=dist/magma-player.cjs.js --platform=browser","build:umd":"esbuild MagmaPlayer.js --bundle --format=iife --global-name=MagmaPlayer --outfile=dist/magma-player.umd.js --platform=browser --minify","build:react":"npm run build:react:js && rm -f react/@react__stub.d.ts","build:angular":"tsc --project tsconfig.angular.json --emitDeclarationOnly && rm -f angular/@angular__core.d.ts angular/*.js","build:react:js":"esbuild react/MagmaPlayer.jsx --bundle --format=esm --outfile=react/MagmaPlayer.js --platform=browser --external:react --external:react-dom --external:../MagmaPlayer.js --jsx=automatic --packages=external","prepublishOnly":"npm run build"},"_npmUser":{"name":"der-lukas","email":"admin@der-lukas.net"},"repository":{"url":"git+https://github.com/der-lukas/magma-player.git","type":"git"},"_npmVersion":"11.5.1","description":"A reusable library for compositing color and alpha mask videos with perfect synchronization using a shared clock","directories":{},"_nodeVersion":"24.5.0","_hasShrinkwrap":false,"devDependencies":{"esbuild":"^0.27.0","typescript":"^5.9.3","@types/node":"^24.10.1"},"peerDependencies":{"react":">=16.8.0","@angular/core":">=17.0.0"},"_npmOperationalInternal":{"tmp":"tmp/magma-player_1.1.1_1764253410382_0.5291600493509854","host":"s3://npm-registry-packages-npm-production"}},"1.1.2":{"name":"@der-lukas/magma-player","version":"1.1.2","keywords":["video","alpha","mask","compositing","webgl","canvas","synchronization","transparency","mp4","video-player"],"author":{"name":"der-lukas"},"license":"MIT","_id":"@der-lukas/magma-player@1.1.2","maintainers":[{"name":"der-lukas","email":"admin@der-lukas.net"}],"homepage":"https://github.com/der-lukas/magma-player#readme","bugs":{"url":"https://github.com/der-lukas/magma-player/issues"},"dist":{"shasum":"ce6ce9f2fb207115fd3a2154ac5383e109585abb","tarball":"https://registry.npmjs.org/@der-lukas/magma-player/-/magma-player-1.1.2.tgz","fileCount":20,"integrity":"sha512-0XbBlsNmuzyLIxtZFwdmLCA4U+oCrWSjNtuWRy6b9kWy7CwQmepRibgbKDgoGIGeTdcFdq8AOhruKvMt6VlHDw==","signatures":[{"sig":"MEQCIEyBdhVZJBxzGhNEIb3BjM01fY7RTZgun4qVjoC4sfPfAiAO1COfX76b5cFIrEr47uZG63V+G5zqhETnxtf0g4hYnw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":245237},"main":"MagmaPlayer.js","types":"MagmaPlayer.d.ts","module":"MagmaPlayer.js","browser":"dist/magma-player.umd.js","engines":{"node":">=14.0.0"},"exports":{".":{"import":"./MagmaPlayer.js","browser":"./dist/magma-player.umd.js","require":"./dist/magma-player.cjs.js"},"./react":"./react/index.js","./angular":{"types":"./angular/index.d.ts","import":"./angular/index.ts"},"./vanilla":"./vanilla/index.js","./package.json":"./package.json"},"gitHead":"74a4550caea5683a36e70977617017861f05f85b","scripts":{"build":"npm run build:react && npm run build:angular && npm run build:cjs && npm run build:umd","build:cjs":"esbuild MagmaPlayer.js --bundle --format=cjs --outfile=dist/magma-player.cjs.js --platform=browser","build:umd":"esbuild MagmaPlayer.js --bundle --format=iife --global-name=MagmaPlayer --outfile=dist/magma-player.umd.js --platform=browser --minify","build:react":"npm run build:react:js && rm -f react/@react__stub.d.ts","build:angular":"node scripts/create-angular-stub.js && tsc --project tsconfig.angular.json --emitDeclarationOnly && rm -f angular/@angular__core.d.ts angular/*.js","build:react:js":"esbuild react/MagmaPlayer.jsx --bundle --format=esm --outfile=react/MagmaPlayer.js --platform=browser --external:react --external:react-dom --external:../MagmaPlayer.js --jsx=automatic --packages=external","prepublishOnly":"npm run build"},"_npmUser":{"name":"der-lukas","email":"admin@der-lukas.net"},"repository":{"url":"git+https://github.com/der-lukas/magma-player.git","type":"git"},"_npmVersion":"11.5.1","description":"A reusable library for compositing color and alpha mask videos with perfect synchronization using a shared clock","directories":{},"_nodeVersion":"24.5.0","_hasShrinkwrap":false,"devDependencies":{"esbuild":"^0.27.0","typescript":"^5.9.3","@types/node":"^24.10.1"},"peerDependencies":{"react":">=16.8.0","@angular/core":">=17.0.0"},"_npmOperationalInternal":{"tmp":"tmp/magma-player_1.1.2_1764258679185_0.16659851415286875","host":"s3://npm-registry-packages-npm-production"}},"1.1.3":{"name":"@der-lukas/magma-player","version":"1.1.3","keywords":["video","alpha","mask","compositing","webgl","canvas","synchronization","transparency","mp4","video-player"],"author":{"name":"der-lukas"},"license":"MIT","_id":"@der-lukas/magma-player@1.1.3","maintainers":[{"name":"der-lukas","email":"admin@der-lukas.net"}],"homepage":"https://github.com/der-lukas/magma-player#readme","bugs":{"url":"https://github.com/der-lukas/magma-player/issues"},"dist":{"shasum":"6b3aab974ff872722d03606500eca0a66d26d949","tarball":"https://registry.npmjs.org/@der-lukas/magma-player/-/magma-player-1.1.3.tgz","fileCount":20,"integrity":"sha512-oD43P7zuvERKtM253hByb3QCUg0Fj2Snj/xCDDtOQMTH8qsNl7dHRfgggU4uz2b5ySpJEZ2wmRs48YCM/L3qmA==","signatures":[{"sig":"MEUCICYbQfKeXb3IIF+PKciNlWamsdM18lB1WI5nagC4xzkCAiEA1aPjBI9IddN1YgBfPu44aNYXx6QFl2yruF7F5YEKrfM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":260206},"main":"MagmaPlayer.js","types":"MagmaPlayer.d.ts","module":"MagmaPlayer.js","browser":"dist/magma-player.umd.js","engines":{"node":">=14.0.0"},"exports":{".":{"import":"./MagmaPlayer.js","browser":"./dist/magma-player.umd.js","require":"./dist/magma-player.cjs.js"},"./react":"./react/index.js","./angular":{"types":"./angular/index.d.ts","import":"./angular/index.ts"},"./vanilla":"./vanilla/index.js","./package.json":"./package.json"},"gitHead":"05f838cbc2bb3d3960382d454793989954a6c98f","scripts":{"build":"npm run build:react && npm run build:angular && npm run build:cjs && npm run build:umd","build:cjs":"esbuild MagmaPlayer.js --bundle --format=cjs --outfile=dist/magma-player.cjs.js --platform=browser","build:umd":"esbuild MagmaPlayer.js --bundle --format=iife --global-name=MagmaPlayer --outfile=dist/magma-player.umd.js --platform=browser --minify","build:react":"npm run build:react:js && rm -f react/@react__stub.d.ts","build:angular":"node scripts/create-angular-stub.js && tsc --project tsconfig.angular.json --emitDeclarationOnly && rm -f angular/@angular__core.d.ts angular/*.js","build:react:js":"esbuild react/MagmaPlayer.jsx --bundle --format=esm --outfile=react/MagmaPlayer.js --platform=browser --external:react --external:react-dom --external:../MagmaPlayer.js --jsx=automatic --packages=external","prepublishOnly":"npm run build"},"_npmUser":{"name":"der-lukas","email":"admin@der-lukas.net"},"repository":{"url":"git+https://github.com/der-lukas/magma-player.git","type":"git"},"_npmVersion":"11.5.1","description":"A reusable library for compositing color and alpha mask videos with perfect synchronization using a shared clock","directories":{},"_nodeVersion":"24.5.0","_hasShrinkwrap":false,"devDependencies":{"esbuild":"^0.27.0","typescript":"^5.9.3","@types/node":"^24.10.1"},"peerDependencies":{"react":">=16.8.0","@angular/core":">=17.0.0"},"_npmOperationalInternal":{"tmp":"tmp/magma-player_1.1.3_1764261123649_0.18169440341420784","host":"s3://npm-registry-packages-npm-production"}},"1.2.0":{"name":"@der-lukas/magma-player","version":"1.2.0","keywords":["video","alpha","mask","compositing","webgl","canvas","synchronization","transparency","mp4","video-player"],"author":{"name":"der-lukas"},"license":"MIT","_id":"@der-lukas/magma-player@1.2.0","maintainers":[{"name":"der-lukas","email":"admin@der-lukas.net"}],"homepage":"https://github.com/der-lukas/magma-player#readme","bugs":{"url":"https://github.com/der-lukas/magma-player/issues"},"dist":{"shasum":"970d5a63b5ec9e6b4bd899eeffb09e43265b2ce9","tarball":"https://registry.npmjs.org/@der-lukas/magma-player/-/magma-player-1.2.0.tgz","fileCount":20,"integrity":"sha512-ga3/Ovzyf65+0YdDkEZLaln79iAS74vkO6730DMtBx08EPbTSF7URkKq1RepwzTOU7EWv7m4bhdAcYwpw2Fkhw==","signatures":[{"sig":"MEYCIQCjX3k6XSZtkQElDBQHrUk0TS7C/5EFlySKlJTbDfNkxwIhALcG6AIuUFOT9YUBR4bkNfQsg2cEYoZY4IjuNUJdJnH+","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":278967},"main":"MagmaPlayer.js","types":"MagmaPlayer.d.ts","module":"MagmaPlayer.js","browser":"dist/magma-player.umd.js","engines":{"node":">=14.0.0"},"exports":{".":{"import":"./MagmaPlayer.js","browser":"./dist/magma-player.umd.js","require":"./dist/magma-player.cjs.js"},"./react":"./react/index.js","./angular":{"types":"./angular/index.d.ts","import":"./angular/index.ts"},"./vanilla":"./vanilla/index.js","./package.json":"./package.json"},"gitHead":"1fa37f80f6ab0b6ccebb5b1c1d3ce776a7df2f45","scripts":{"build":"npm run build:react && npm run build:angular && npm run build:cjs && npm run build:umd","build:cjs":"esbuild MagmaPlayer.js --bundle --format=cjs --outfile=dist/magma-player.cjs.js --platform=browser","build:umd":"esbuild MagmaPlayer.js --bundle --format=iife --global-name=MagmaPlayer --outfile=dist/magma-player.umd.js --platform=browser --minify","build:react":"npm run build:react:js && rm -f react/@react__stub.d.ts","build:angular":"node scripts/create-angular-stub.js && tsc --project tsconfig.angular.json --emitDeclarationOnly && rm -f angular/@angular__core.d.ts angular/*.js","build:react:js":"esbuild react/MagmaPlayer.jsx --bundle --format=esm --outfile=react/MagmaPlayer.js --platform=browser --external:react --external:react-dom --external:../MagmaPlayer.js --jsx=automatic --packages=external","prepublishOnly":"npm run build"},"_npmUser":{"name":"der-lukas","email":"admin@der-lukas.net"},"repository":{"url":"git+https://github.com/der-lukas/magma-player.git","type":"git"},"_npmVersion":"11.6.2","description":"A reusable library for compositing color and alpha mask videos with perfect synchronization using a shared clock","directories":{},"_nodeVersion":"24.11.1","_hasShrinkwrap":false,"devDependencies":{"esbuild":"^0.27.0","typescript":"^5.9.3","@types/node":"^24.10.1"},"peerDependencies":{"react":">=16.8.0","@angular/core":">=17.0.0"},"_npmOperationalInternal":{"tmp":"tmp/magma-player_1.2.0_1764327519289_0.767692815918549","host":"s3://npm-registry-packages-npm-production"}},"1.2.1":{"name":"@der-lukas/magma-player","version":"1.2.1","description":"A reusable library for compositing color and alpha mask videos with perfect synchronization using a shared clock","main":"MagmaPlayer.js","module":"MagmaPlayer.js","types":"MagmaPlayer.d.ts","browser":"dist/magma-player.umd.js","exports":{".":{"browser":"./dist/magma-player.umd.js","import":"./MagmaPlayer.js","require":"./dist/magma-player.cjs.js"},"./react":"./react/index.js","./angular":{"import":"./angular/index.ts","types":"./angular/index.d.ts"},"./vanilla":"./vanilla/index.js","./package.json":"./package.json"},"scripts":{"build":"npm run build:react && npm run build:angular && npm run build:cjs && npm run build:umd","build:react":"npm run build:react:js && rm -f react/@react__stub.d.ts","build:react:js":"esbuild react/MagmaPlayer.jsx --bundle --format=esm --outfile=react/MagmaPlayer.js --platform=browser --external:react --external:react-dom --external:../MagmaPlayer.js --jsx=automatic --packages=external","build:angular":"node scripts/create-angular-stub.js && tsc --project tsconfig.angular.json --emitDeclarationOnly && rm -f angular/@angular__core.d.ts angular/*.js","build:cjs":"esbuild MagmaPlayer.js --bundle --format=cjs --outfile=dist/magma-player.cjs.js --platform=browser","build:umd":"esbuild MagmaPlayer.js --bundle --format=iife --global-name=MagmaPlayer --outfile=dist/magma-player.umd.js --platform=browser --minify","prepublishOnly":"npm run build"},"keywords":["video","alpha","mask","compositing","webgl","canvas","synchronization","transparency","mp4","video-player"],"author":{"name":"der-lukas"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/der-lukas/magma-player.git"},"engines":{"node":">=14.0.0"},"peerDependencies":{"@angular/core":">=17.0.0","react":">=16.8.0"},"devDependencies":{"@types/node":"^24.10.1","esbuild":"^0.27.0","typescript":"^5.9.3"},"gitHead":"3ea44535e12f4d2ffdf929bd70493756dd183a46","_id":"@der-lukas/magma-player@1.2.1","bugs":{"url":"https://github.com/der-lukas/magma-player/issues"},"homepage":"https://github.com/der-lukas/magma-player#readme","_nodeVersion":"24.11.1","_npmVersion":"11.6.2","dist":{"integrity":"sha512-BTAv3Jb5L6DnSI3b4qWplRgaYz1GzrmPC9VuoBdo+nLfM2aLojXB+tMWQOx9GfYaQcxZxDa7ttiQ7uanqPZV0Q==","shasum":"ad013a5d51f24148eead412be933dc1764f81320","tarball":"https://registry.npmjs.org/@der-lukas/magma-player/-/magma-player-1.2.1.tgz","fileCount":20,"unpackedSize":284292,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCmfLo+xRN3BHFvmuv1imc5anajrwjc2cRumBrMgCh0tQIgW4686HigD04//L9aSuyOHFh988+yClC8WH/joPRtRps="}]},"_npmUser":{"name":"der-lukas","email":"admin@der-lukas.net"},"directories":{},"maintainers":[{"name":"der-lukas","email":"admin@der-lukas.net"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/magma-player_1.2.1_1764328454185_0.2099733401599233"},"_hasShrinkwrap":false}},"time":{"created":"2025-11-27T12:06:54.697Z","modified":"2025-11-28T11:14:14.556Z","1.0.0":"2025-11-27T12:06:54.947Z","1.0.1":"2025-11-27T12:11:27.070Z","1.1.0":"2025-11-27T14:05:41.691Z","1.1.1":"2025-11-27T14:23:30.567Z","1.1.2":"2025-11-27T15:51:19.446Z","1.1.3":"2025-11-27T16:32:03.870Z","1.2.0":"2025-11-28T10:58:39.481Z","1.2.1":"2025-11-28T11:14:14.358Z"},"bugs":{"url":"https://github.com/der-lukas/magma-player/issues"},"author":{"name":"der-lukas"},"license":"MIT","homepage":"https://github.com/der-lukas/magma-player#readme","keywords":["video","alpha","mask","compositing","webgl","canvas","synchronization","transparency","mp4","video-player"],"repository":{"type":"git","url":"git+https://github.com/der-lukas/magma-player.git"},"description":"A reusable library for compositing color and alpha mask videos with perfect synchronization using a shared clock","maintainers":[{"name":"der-lukas","email":"admin@der-lukas.net"}],"readme":"# Magma Player\n\nA reusable library for compositing color and alpha mask videos with perfect synchronization using a shared clock.\n\n## Features\n\n- ✅ **Perfect Sync** - Uses `performance.now()` shared clock to eliminate desync\n- ✅ **WebGL Compositing** - Automatic WebGL rendering for best performance (WebGL2 when available)\n- ✅ **Canvas2D Fallback** - Falls back to Canvas2D if WebGL unavailable\n- ✅ **Framework Agnostic** - Works in React, Vue, vanilla JS, or any framework\n- ✅ **TypeScript Support** - Full TypeScript definitions included\n- ✅ **Small Bundle** - Lightweight, no heavy dependencies\n- ✅ **Cross-Browser** - Works in all modern browsers (Chrome, Firefox, Safari, Edge)\n- ✅ **Performance Optimized** - Frame throttling, texture caching, canvas reuse\n- ✅ **Retina Support** - Automatic pixel ratio scaling for high-DPI displays\n- ✅ **Event System** - Full event emitter API for playback events\n- ✅ **Rich API** - Volume, playback speed, time/duration getters, loop control\n- ✅ **Error Handling** - Custom error class with error codes for programmatic handling\n\n## Installation\n\n### Option 1: npm\n\n```bash\nnpm install @der-lukas/magma-player\n```\n\n### Option 2: Copy files directly\n\nCopy the `magma` folder to your project:\n\n```\nsrc/\n  magma/\n    MagmaPlayer.js\n    index.js\n```\n\n### Option 3: Use from CDN (UMD build)\n\n```html\n<script src=\"https://cdn.example.com/magma-player.umd.js\"></script>\n<script>\n  const player = new MagmaPlayer.MagmaPlayer({ ... });\n</script>\n```\n\n## Usage\n\n### Vanilla JavaScript (Using Helper - Recommended)\n\n```html\n<!DOCTYPE html>\n<html>\n  <head>\n    <title>Magma Player Example</title>\n  </head>\n  <body>\n    <canvas id=\"player-canvas\"></canvas>\n\n    <script type=\"module\">\n      import { createMagmaPlayer } from \"@der-lukas/magma-player/vanilla\";\n      // Or: import { createMagmaPlayer } from \"./magma/vanilla/createMagmaPlayer.js\";\n\n      const { player } = createMagmaPlayer({\n        colorVideoSrc: \"color.mp4\",\n        maskVideoSrc: \"mask.mp4\",\n        canvas: \"#player-canvas\", // Can use selector or element\n        useWebGL: true,\n        autoplay: false,\n        autoCleanup: true, // Default: automatically cleanup on page unload\n        onReady: () => console.log(\"Player ready!\"),\n        onError: (error) => console.error(\"Error:\", error),\n      });\n\n      // Listen to events\n      player.on(\"play\", () => console.log(\"Playing\"));\n      player.on(\"pause\", () => console.log(\"Paused\"));\n      player.on(\"timeupdate\", (time) => console.log(\"Time:\", time));\n\n      // Control playback\n      player.play();\n      // Automatic cleanup on page unload (if autoCleanup: true)!\n    </script>\n  </body>\n</html>\n```\n\n### Vanilla JavaScript (Direct Usage - Full Control)\n\nUse `new MagmaPlayer()` directly when you need:\n\n- Full access to all options (e.g., `waitForCanvas`, `canvasTimeout`)\n- Canvas as a function (for dynamic canvas resolution)\n- Custom cleanup logic\n- Building framework wrappers\n\n```html\n<!DOCTYPE html>\n<html>\n  <head>\n    <title>Magma Player Example</title>\n  </head>\n  <body>\n    <canvas id=\"player-canvas\"></canvas>\n\n    <script type=\"module\">\n      import { MagmaPlayer } from \"@der-lukas/magma-player\";\n      // Or: import { MagmaPlayer } from \"./magma/MagmaPlayer.js\";\n\n      // MagmaPlayer supports canvas as element, selector, or function\n      const player = new MagmaPlayer({\n        colorVideoSrc: \"color.mp4\",\n        maskVideoSrc: \"mask.mp4\",\n        canvas: \"#player-canvas\", // Can use selector directly!\n        // Or: canvas: document.getElementById(\"player-canvas\")\n        // Or: canvas: () => document.getElementById(\"player-canvas\")\n        // Or: canvas: canvas, waitForCanvas: true, canvasTimeout: 10000\n        useWebGL: true,\n        autoplay: false,\n        targetFPS: 60,\n        onReady: () => console.log(\"Player ready!\"),\n        onError: (error) => console.error(\"Error:\", error),\n      });\n\n      player.on(\"play\", () => console.log(\"Playing\"));\n      player.on(\"pause\", () => console.log(\"Paused\"));\n      player.on(\"timeupdate\", (time) => console.log(\"Time:\", time));\n\n      player.play();\n\n      // Manual cleanup\n      window.addEventListener(\"beforeunload\", () => player.destroy());\n    </script>\n  </body>\n</html>\n```\n\n### Hover Interaction Example\n\n```javascript\nconst canvas = document.getElementById(\"player-canvas\");\nconst player = new MagmaPlayer({\n  colorVideoSrc: \"color.mp4\",\n  maskVideoSrc: \"mask.mp4\",\n  canvas: canvas,\n  autoplay: false, // Important: disable autoplay for hover control\n});\n\n// User implements hover themselves\ncanvas.addEventListener(\"mouseenter\", () => player.play());\ncanvas.addEventListener(\"mouseleave\", () => player.reset());\n```\n\n### TypeScript\n\n```typescript\nimport {\n  MagmaPlayer,\n  MagmaPlayerError,\n  ERROR_CODES,\n} from \"@der-lukas/magma-player\";\n// Or: import { MagmaPlayer, MagmaPlayerError, ERROR_CODES } from './magma/MagmaPlayer.js';\n\nconst canvas = document.getElementById(\"player-canvas\") as HTMLCanvasElement;\n\ntry {\n  const player = new MagmaPlayer({\n    colorVideoSrc: \"color.mp4\",\n    maskVideoSrc: \"mask.mp4\",\n    canvas: canvas,\n    useWebGL: true,\n    autoplay: false,\n    strictDuration: true, // TypeScript will enforce type safety\n  });\n\n  player.on(\"ready\", () => {\n    console.log(\"Player ready!\");\n  });\n\n  player.on(\"error\", (error: MagmaPlayerError) => {\n    if (error.code === ERROR_CODES.VIDEO_LOAD_FAILED) {\n      console.error(\"Video failed to load:\", error.details);\n    }\n  });\n} catch (error) {\n  if (error instanceof MagmaPlayerError) {\n    console.error(\"MagmaPlayer error:\", error.code, error.message);\n  }\n}\n```\n\n### React (Using Component - Simplest)\n\n```jsx\nimport { MagmaPlayer } from \"@der-lukas/magma-player/react\";\n// Or: import { MagmaPlayer } from \"./magma/react/MagmaPlayer.jsx\";\n\nfunction VideoPlayer({ colorSrc, maskSrc }) {\n  const playerRef = React.useRef(null);\n\n  return (\n    <div>\n      <MagmaPlayer\n        ref={playerRef}\n        colorVideoSrc={colorSrc}\n        maskVideoSrc={maskSrc}\n        autoplay={false}\n        useWebGL={true}\n        repeatCount={-1} // Infinite loop\n        onReady={(player) => console.log(\"Ready:\", player)}\n        onError={(error) => console.error(\"Error:\", error)}\n        style={{ maxWidth: \"100%\" }}\n      />\n      <button onClick={() => playerRef.current?.play()}>Play</button>\n      <button onClick={() => playerRef.current?.setRepeatCount(3)}>\n        Repeat 3x\n      </button>\n    </div>\n  );\n}\n```\n\n### React (Using Hook - More Control)\n\n```jsx\nimport { useMagmaPlayer } from \"@der-lukas/magma-player/react\";\n// Or: import { useMagmaPlayer } from \"./magma/react/useMagmaPlayer.js\";\n\nfunction VideoPlayer({ colorSrc, maskSrc }) {\n  const { canvasRef, isReady, isPlaying, play, pause, setRepeatCount, error } =\n    useMagmaPlayer({\n      colorVideoSrc: colorSrc,\n      maskVideoSrc: maskSrc,\n      autoplay: false,\n      useWebGL: true,\n    });\n\n  return (\n    <div>\n      <canvas ref={canvasRef} style={{ maxWidth: \"100%\" }} />\n      {isReady && (\n        <>\n          <button onClick={isPlaying ? pause : play}>\n            {isPlaying ? \"Pause\" : \"Play\"}\n          </button>\n          <button onClick={() => setRepeatCount(-1)}>Infinite Loop</button>\n          <button onClick={() => setRepeatCount(3)}>Repeat 3x</button>\n        </>\n      )}\n      {error && <div>Error: {error.message}</div>}\n    </div>\n  );\n}\n```\n\n### React (Direct Usage - Full Control)\n\nUse `new MagmaPlayer()` directly when you need:\n\n- Full access to all options (e.g., `waitForCanvas`, `canvasTimeout`)\n- Custom lifecycle management\n- Building custom hooks or components\n\n```jsx\nimport { useRef, useEffect } from \"react\";\nimport { MagmaPlayer } from \"@der-lukas/magma-player\";\n// Or: import { MagmaPlayer } from \"./magma/MagmaPlayer.js\";\n\nfunction VideoPlayer({ colorSrc, maskSrc }) {\n  const canvasRef = useRef(null);\n  const playerRef = useRef(null);\n\n  useEffect(() => {\n    if (!canvasRef.current) return;\n\n    const player = new MagmaPlayer({\n      colorVideoSrc: colorSrc,\n      maskVideoSrc: maskSrc,\n      canvas: canvasRef.current,\n      // Or: canvas: () => canvasRef.current, waitForCanvas: true\n      useWebGL: true,\n      autoplay: false,\n    });\n\n    player.on(\"ready\", () => console.log(\"Ready\"));\n    player.on(\"error\", (error) => console.error(\"Error:\", error));\n\n    playerRef.current = player;\n\n    return () => {\n      player.destroy();\n    };\n  }, [colorSrc, maskSrc]);\n\n  return <canvas ref={canvasRef} style={{ maxWidth: \"100%\" }} />;\n}\n```\n\n### Vue\n\n```vue\n<template>\n  <canvas ref=\"canvas\"></canvas>\n</template>\n\n<script setup>\nimport { ref, onMounted, onUnmounted } from \"vue\";\nimport { MagmaPlayer } from \"@der-lukas/magma-player\";\n// Or: import { MagmaPlayer } from './magma/MagmaPlayer.js';\n\nconst canvas = ref(null);\nlet player = null;\n\nonMounted(() => {\n  player = new MagmaPlayer({\n    colorVideoSrc: \"color.mp4\",\n    maskVideoSrc: \"mask.mp4\",\n    canvas: canvas.value,\n    useWebGL: true,\n    autoplay: false,\n  });\n\n  player.on(\"ready\", () => console.log(\"Ready\"));\n});\n\nonUnmounted(() => {\n  if (player) {\n    player.destroy();\n  }\n});\n</script>\n```\n\n### Angular (Using Component - Recommended)\n\n```typescript\nimport { Component } from \"@angular/core\";\nimport { MagmaPlayerComponent } from \"@der-lukas/magma-player/angular\";\nimport type { MagmaPlayer } from \"@der-lukas/magma-player\";\n// Or: import { MagmaPlayerComponent } from \"./magma/angular/magma-player.component\";\n// Or: import type { MagmaPlayer } from \"./magma/MagmaPlayer.js\";\n\n@Component({\n  selector: \"app-video-player\",\n  standalone: true,\n  imports: [MagmaPlayerComponent],\n  template: `\n    <magma-player\n      [colorVideoSrc]=\"'color.mp4'\"\n      [maskVideoSrc]=\"'mask.mp4'\"\n      [fixedSize]=\"{ width: 800, height: 600 }\"\n      [autoplay]=\"false\"\n      [useWebGL]=\"true\"\n      [repeatCount]=\"-1\"\n      (ready)=\"onPlayerReady($event)\"\n      (error)=\"onPlayerError($event)\"\n      (play)=\"onPlay()\"\n      (pause)=\"onPause()\"\n      (timeupdate)=\"onTimeUpdate($event)\"\n    />\n    <button (click)=\"playerRef?.startPlayback()\">Play</button>\n    <button (click)=\"playerRef?.pausePlayback()\">Pause</button>\n    <button (click)=\"playerRef?.setRepeatCount(3)\">Repeat 3x</button>\n  `,\n})\nexport class VideoPlayerComponent {\n  playerRef: MagmaPlayerComponent | null = null;\n\n  onPlayerReady(player: MagmaPlayer) {\n    console.log(\"Player ready:\", player);\n    // Access the component instance to control playback\n    // Note: You'll need to use @ViewChild to get the component reference\n  }\n\n  onPlayerError(error: any) {\n    console.error(\"Player error:\", error);\n  }\n\n  onPlay() {\n    console.log(\"Playing\");\n  }\n\n  onPause() {\n    console.log(\"Paused\");\n  }\n\n  onTimeUpdate(time: number) {\n    console.log(\"Time:\", time);\n  }\n}\n```\n\n**With ViewChild for programmatic control:**\n\n```typescript\nimport { Component, ViewChild } from \"@angular/core\";\nimport { MagmaPlayerComponent } from \"@der-lukas/magma-player/angular\";\n// Or: import { MagmaPlayerComponent } from \"./magma/angular/magma-player.component\";\n\n@Component({\n  selector: \"app-video-player\",\n  standalone: true,\n  imports: [MagmaPlayerComponent],\n  template: `\n    <magma-player\n      #player\n      [colorVideoSrc]=\"'color.mp4'\"\n      [maskVideoSrc]=\"'mask.mp4'\"\n      [autoplay]=\"false\"\n    />\n    <button (click)=\"player.startPlayback()\">Play</button>\n    <button (click)=\"player.pausePlayback()\">Pause</button>\n    <button (click)=\"player.seek(10)\">Seek to 10s</button>\n  `,\n})\nexport class VideoPlayerComponent {\n  @ViewChild(\"player\") player!: MagmaPlayerComponent;\n}\n```\n\n**Reactive signals available on the component:**\n\n```typescript\n// Access reactive state\nplayer.isPlaying(); // Signal<boolean>\nplayer.isReady(); // Signal<boolean>\nplayer.currentTime(); // Signal<number>\nplayer.duration(); // Signal<number>\nplayer.videoWidth(); // Signal<number>\nplayer.videoHeight(); // Signal<number>\nplayer.volume(); // Signal<number>\nplayer.playbackRate(); // Signal<number>\n```\n\n### Angular (Direct Usage - Full Control)\n\nUse `new MagmaPlayer()` directly when you need full control over the lifecycle:\n\n```typescript\nimport {\n  Component,\n  ElementRef,\n  OnDestroy,\n  OnInit,\n  ViewChild,\n} from \"@angular/core\";\nimport { MagmaPlayer } from \"@der-lukas/magma-player\";\n// Or: import { MagmaPlayer } from \"./magma/MagmaPlayer.js\";\n\n@Component({\n  selector: \"app-video-player\",\n  template: `<canvas #canvas></canvas>`,\n})\nexport class VideoPlayerComponent implements OnInit, OnDestroy {\n  @ViewChild(\"canvas\", { static: false })\n  canvasRef!: ElementRef<HTMLCanvasElement>;\n  private player: MagmaPlayer | null = null;\n\n  ngOnInit() {\n    // Wait for view to initialize\n    setTimeout(() => {\n      if (this.canvasRef?.nativeElement) {\n        this.player = new MagmaPlayer({\n          colorVideoSrc: \"color.mp4\",\n          maskVideoSrc: \"mask.mp4\",\n          canvas: this.canvasRef.nativeElement,\n          useWebGL: true,\n          autoplay: false,\n        });\n\n        this.player.on(\"ready\", () => console.log(\"Ready\"));\n        this.player.on(\"error\", (error) => console.error(\"Error:\", error));\n      }\n    });\n  }\n\n  ngOnDestroy() {\n    this.player?.destroy();\n  }\n}\n```\n\n## API\n\n### Constructor Options\n\n```typescript\nnew MagmaPlayer({\n  colorVideoSrc: string,      // URL to color video (MP4)\n  maskVideoSrc: string,        // URL to alpha mask video (MP4, grayscale)\n  canvas: HTMLCanvasElement,   // Canvas element to render to\n  useWebGL?: boolean,          // Use WebGL (default: true)\n  autoplay?: boolean,          // Autoplay on ready (default: true)\n  targetFPS?: number,          // Target frame rate (default: 60, use 30/24 for battery saving)\n  onReady?: () => void,        // Callback when player is ready\n  onError?: (error) => void,   // Callback for errors (receives MagmaPlayerError)\n  strictDuration?: boolean,    // Throw error if video durations don't match (default: false)\n  pauseWhenHidden?: boolean,   // Pause rendering when canvas is not visible (default: true)\n})\n```\n\n### Playback Control Methods\n\n- `play()` - Start playback\n- `pause()` - Pause playback\n- `seek(time)` - Seek to specific time (in seconds)\n- `reset()` - Reset to time 0 and pause\n\n### Volume Control\n\n- `setVolume(volume)` - Set volume (0-1 range)\n- `getVolume()` - Get current volume (0-1)\n\n### Playback Speed Control\n\n- `setPlaybackRate(rate)` - Set playback speed (0.25-4.0 range, default: 1.0)\n- `getPlaybackRate()` - Get current playback rate\n\n### Time & Duration Getters\n\n- `getCurrentTime()` - Get current playback time (in seconds)\n- `getDuration()` - Get video duration (in seconds)\n- `getBuffered()` - Get buffered time ranges (array of `{start, end}` objects)\n\n### Loop Control\n\n- `setLoop(loop)` - Enable/disable infinite looping (boolean, default: true)\n- `getLoop()` - Get current loop setting\n\n### Repeat Control\n\n- `setRepeatCount(count)` - Set total number of times to play (-1 = infinite loop, 0 = don't play, 1 = play once, >1 = play that many times total)\n- `getRepeatCount()` - Get current repeat count\n\n**Semantics (aligned with CSS `animation-iteration-count`):**\n\n- `-1`: Infinite loop (plays forever)\n- `0`: Don't play (0 iterations) - aligns with CSS\n- `1`: Play once and stop (1 iteration)\n- `3`: Play 3 times total (3 iterations)\n\n```javascript\nplayer.setRepeatCount(3); // Play 3 times total\nplayer.setRepeatCount(-1); // Infinite loop\nplayer.setRepeatCount(1); // Play once\nplayer.setRepeatCount(0); // Don't play (0 iterations)\n```\n\n### Utility Methods\n\n- `isPlaying()` - Check if currently playing\n- `isPaused()` - Check if currently paused\n- `getVideoWidth()` - Get video width in pixels\n- `getVideoHeight()` - Get video height in pixels\n- `setSize(width, height)` - Manually set canvas size\n\n### Event System\n\n- `on(event, callback)` - Add event listener\n- `off(event, callback)` - Remove event listener\n\n**Available Events:**\n\n- `ready` - Player is ready and initialized\n- `play` - Playback started\n- `pause` - Playback paused\n- `timeupdate` - Playback time updated (callback receives current time)\n- `ended` - Playback ended (only if loop is disabled)\n- `seeked` - Seek operation completed (callback receives seek time)\n- `error` - Error occurred (callback receives error object)\n\n### Cleanup\n\n- `destroy()` - Clean up and destroy player instance (removes event listeners, stops playback, frees resources)\n\n### Error Handling\n\nMagmaPlayer uses a custom error class `MagmaPlayerError` with error codes for programmatic error handling:\n\n```javascript\nimport { MagmaPlayer, MagmaPlayerError, ERROR_CODES } from './magma/MagmaPlayer.js';\n\ntry {\n  const player = new MagmaPlayer({ ... });\n} catch (error) {\n  if (error instanceof MagmaPlayerError) {\n    switch (error.code) {\n      case ERROR_CODES.INVALID_INPUT:\n        console.error('Invalid input:', error.message);\n        break;\n      case ERROR_CODES.VIDEO_LOAD_FAILED:\n        console.error('Video failed to load:', error.details);\n        break;\n      // ... other error codes\n    }\n  }\n}\n```\n\n**Error Codes:**\n\n- `INVALID_INPUT` - Invalid constructor parameters\n- `VIDEO_LOAD_FAILED` - Video failed to load\n- `VIDEO_LOAD_TIMEOUT` - Video load timeout (30 seconds)\n- `DURATION_MISMATCH` - Video durations don't match (only if `strictDuration: true`)\n- `WEBGL_INIT_FAILED` - WebGL initialization failed (automatically falls back to Canvas2D)\n- `CANVAS_REMOVED` - Canvas was removed from DOM during playback\n\n## How It Works\n\n1. **Shared Clock**: Uses `performance.now()` as a master clock to drive both videos\n2. **Synchronization**: Both videos are synced to the same time using the shared clock\n3. **Compositing**:\n   - **WebGL**: Uploads both videos as textures and composites using a fragment shader (WebGL2 when available)\n   - **Canvas2D**: Falls back to pixel-by-pixel compositing if WebGL unavailable\n4. **Perfect Sync**: Videos never drift because they're both driven by the same clock\n5. **Performance**: Texture uploads are cached, temp canvases are reused, frame throttling for low-end devices\n\n## Performance Tips\n\n1. **Frame Rate**: Use `targetFPS: 30` or `targetFPS: 24` for battery saving on mobile devices\n2. **Autoplay**: Set `autoplay: false` if you want to control playback manually (e.g., on hover)\n3. **WebGL**: WebGL is automatically used when available for best performance\n4. **Retina Displays**: Pixel ratio scaling is automatic - no configuration needed\n5. **Video Format**: Use H.264 MP4 with yuv420p for best compatibility and performance\n\n## Browser Support\n\n- ✅ Chrome/Edge: Full support (WebGL2 available)\n- ✅ Firefox: Full support (WebGL2 available)\n- ✅ Safari 16.4+: Full support (WebGL2 available)\n- ✅ iOS Safari 16.4+: Full support\n\n**Note**: Older browsers will automatically fall back to Canvas2D rendering if WebGL is unavailable.\n\n## Video Format Requirements\n\n- **Format**: MP4 (H.264, yuv420p)\n- **Color Video**: Standard RGB video\n- **Mask Video**: Grayscale video (white = opaque, black = transparent)\n- **Dimensions**: Both videos must have identical dimensions\n- **FPS**: Both videos must have identical frame rates\n- **Duration**: Both videos must have identical durations (warnings shown if mismatch > 0.1s)\n\n## Troubleshooting\n\n### Videos don't load\n\n- **Check CORS**: Ensure videos are served with proper CORS headers if loading from different domain\n- **Check format**: Videos must be MP4 (H.264). Check browser console for codec errors\n- **Check paths**: Verify video URLs are correct and accessible\n\n### Autoplay doesn't work\n\n- **Browser restrictions**: Many browsers block autoplay. Use `autoplay: false` and call `play()` after user interaction\n- **Mobile devices**: iOS requires user interaction before playback can start\n\n### Videos are out of sync\n\n- **Check dimensions**: Videos must have identical dimensions\n- **Check duration**: Videos should have identical durations (warnings shown if mismatch)\n- **Check frame rate**: Videos should have identical frame rates\n\n### WebGL not working\n\n- **Automatic fallback**: Player automatically falls back to Canvas2D if WebGL unavailable\n- **Context loss**: Player automatically handles WebGL context loss and restoration\n- **Performance**: Canvas2D is slower but works everywhere\n\n### Performance issues\n\n- **Lower frame rate**: Use `targetFPS: 30` or `24` for better battery life\n- **Check video size**: Large videos may cause performance issues\n- **WebGL**: Ensure WebGL is available (check browser console for errors)\n\n## License\n\nMIT\n","readmeFilename":"README.md"}