Mirror: A frag-canvas custom element to apply Shadertoy fragment shaders to a canvas or image/video element

Initial Commit

kitten.sh c59fd2cb

+1
.gitignore
···
+
node_modules
+13
package.json
···
+
{
+
"name": "frag-canvas-element",
+
"sideEffects": true,
+
"version": "0.1.0",
+
"main": "index.js",
+
"keywords": [],
+
"author": "",
+
"license": "ISC",
+
"devDependencies": {
+
"typescript": "^5.7.3",
+
"vite": "^6.2.0"
+
}
+
}
+556
pnpm-lock.yaml
···
+
lockfileVersion: '9.0'
+
+
settings:
+
autoInstallPeers: true
+
excludeLinksFromLockfile: false
+
+
importers:
+
+
.:
+
devDependencies:
+
typescript:
+
specifier: ^5.7.3
+
version: 5.7.3
+
vite:
+
specifier: ^6.2.0
+
version: 6.2.0
+
+
packages:
+
+
'@esbuild/aix-ppc64@0.25.0':
+
resolution: {integrity: sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==}
+
engines: {node: '>=18'}
+
cpu: [ppc64]
+
os: [aix]
+
+
'@esbuild/android-arm64@0.25.0':
+
resolution: {integrity: sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==}
+
engines: {node: '>=18'}
+
cpu: [arm64]
+
os: [android]
+
+
'@esbuild/android-arm@0.25.0':
+
resolution: {integrity: sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==}
+
engines: {node: '>=18'}
+
cpu: [arm]
+
os: [android]
+
+
'@esbuild/android-x64@0.25.0':
+
resolution: {integrity: sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==}
+
engines: {node: '>=18'}
+
cpu: [x64]
+
os: [android]
+
+
'@esbuild/darwin-arm64@0.25.0':
+
resolution: {integrity: sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==}
+
engines: {node: '>=18'}
+
cpu: [arm64]
+
os: [darwin]
+
+
'@esbuild/darwin-x64@0.25.0':
+
resolution: {integrity: sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==}
+
engines: {node: '>=18'}
+
cpu: [x64]
+
os: [darwin]
+
+
'@esbuild/freebsd-arm64@0.25.0':
+
resolution: {integrity: sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==}
+
engines: {node: '>=18'}
+
cpu: [arm64]
+
os: [freebsd]
+
+
'@esbuild/freebsd-x64@0.25.0':
+
resolution: {integrity: sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==}
+
engines: {node: '>=18'}
+
cpu: [x64]
+
os: [freebsd]
+
+
'@esbuild/linux-arm64@0.25.0':
+
resolution: {integrity: sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==}
+
engines: {node: '>=18'}
+
cpu: [arm64]
+
os: [linux]
+
+
'@esbuild/linux-arm@0.25.0':
+
resolution: {integrity: sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==}
+
engines: {node: '>=18'}
+
cpu: [arm]
+
os: [linux]
+
+
'@esbuild/linux-ia32@0.25.0':
+
resolution: {integrity: sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==}
+
engines: {node: '>=18'}
+
cpu: [ia32]
+
os: [linux]
+
+
'@esbuild/linux-loong64@0.25.0':
+
resolution: {integrity: sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==}
+
engines: {node: '>=18'}
+
cpu: [loong64]
+
os: [linux]
+
+
'@esbuild/linux-mips64el@0.25.0':
+
resolution: {integrity: sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==}
+
engines: {node: '>=18'}
+
cpu: [mips64el]
+
os: [linux]
+
+
'@esbuild/linux-ppc64@0.25.0':
+
resolution: {integrity: sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==}
+
engines: {node: '>=18'}
+
cpu: [ppc64]
+
os: [linux]
+
+
'@esbuild/linux-riscv64@0.25.0':
+
resolution: {integrity: sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==}
+
engines: {node: '>=18'}
+
cpu: [riscv64]
+
os: [linux]
+
+
'@esbuild/linux-s390x@0.25.0':
+
resolution: {integrity: sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==}
+
engines: {node: '>=18'}
+
cpu: [s390x]
+
os: [linux]
+
+
'@esbuild/linux-x64@0.25.0':
+
resolution: {integrity: sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==}
+
engines: {node: '>=18'}
+
cpu: [x64]
+
os: [linux]
+
+
'@esbuild/netbsd-arm64@0.25.0':
+
resolution: {integrity: sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==}
+
engines: {node: '>=18'}
+
cpu: [arm64]
+
os: [netbsd]
+
+
'@esbuild/netbsd-x64@0.25.0':
+
resolution: {integrity: sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==}
+
engines: {node: '>=18'}
+
cpu: [x64]
+
os: [netbsd]
+
+
'@esbuild/openbsd-arm64@0.25.0':
+
resolution: {integrity: sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==}
+
engines: {node: '>=18'}
+
cpu: [arm64]
+
os: [openbsd]
+
+
'@esbuild/openbsd-x64@0.25.0':
+
resolution: {integrity: sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==}
+
engines: {node: '>=18'}
+
cpu: [x64]
+
os: [openbsd]
+
+
'@esbuild/sunos-x64@0.25.0':
+
resolution: {integrity: sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==}
+
engines: {node: '>=18'}
+
cpu: [x64]
+
os: [sunos]
+
+
'@esbuild/win32-arm64@0.25.0':
+
resolution: {integrity: sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==}
+
engines: {node: '>=18'}
+
cpu: [arm64]
+
os: [win32]
+
+
'@esbuild/win32-ia32@0.25.0':
+
resolution: {integrity: sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==}
+
engines: {node: '>=18'}
+
cpu: [ia32]
+
os: [win32]
+
+
'@esbuild/win32-x64@0.25.0':
+
resolution: {integrity: sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==}
+
engines: {node: '>=18'}
+
cpu: [x64]
+
os: [win32]
+
+
'@rollup/rollup-android-arm-eabi@4.34.8':
+
resolution: {integrity: sha512-q217OSE8DTp8AFHuNHXo0Y86e1wtlfVrXiAlwkIvGRQv9zbc6mE3sjIVfwI8sYUyNxwOg0j/Vm1RKM04JcWLJw==}
+
cpu: [arm]
+
os: [android]
+
+
'@rollup/rollup-android-arm64@4.34.8':
+
resolution: {integrity: sha512-Gigjz7mNWaOL9wCggvoK3jEIUUbGul656opstjaUSGC3eT0BM7PofdAJaBfPFWWkXNVAXbaQtC99OCg4sJv70Q==}
+
cpu: [arm64]
+
os: [android]
+
+
'@rollup/rollup-darwin-arm64@4.34.8':
+
resolution: {integrity: sha512-02rVdZ5tgdUNRxIUrFdcMBZQoaPMrxtwSb+/hOfBdqkatYHR3lZ2A2EGyHq2sGOd0Owk80oV3snlDASC24He3Q==}
+
cpu: [arm64]
+
os: [darwin]
+
+
'@rollup/rollup-darwin-x64@4.34.8':
+
resolution: {integrity: sha512-qIP/elwR/tq/dYRx3lgwK31jkZvMiD6qUtOycLhTzCvrjbZ3LjQnEM9rNhSGpbLXVJYQ3rq39A6Re0h9tU2ynw==}
+
cpu: [x64]
+
os: [darwin]
+
+
'@rollup/rollup-freebsd-arm64@4.34.8':
+
resolution: {integrity: sha512-IQNVXL9iY6NniYbTaOKdrlVP3XIqazBgJOVkddzJlqnCpRi/yAeSOa8PLcECFSQochzqApIOE1GHNu3pCz+BDA==}
+
cpu: [arm64]
+
os: [freebsd]
+
+
'@rollup/rollup-freebsd-x64@4.34.8':
+
resolution: {integrity: sha512-TYXcHghgnCqYFiE3FT5QwXtOZqDj5GmaFNTNt3jNC+vh22dc/ukG2cG+pi75QO4kACohZzidsq7yKTKwq/Jq7Q==}
+
cpu: [x64]
+
os: [freebsd]
+
+
'@rollup/rollup-linux-arm-gnueabihf@4.34.8':
+
resolution: {integrity: sha512-A4iphFGNkWRd+5m3VIGuqHnG3MVnqKe7Al57u9mwgbyZ2/xF9Jio72MaY7xxh+Y87VAHmGQr73qoKL9HPbXj1g==}
+
cpu: [arm]
+
os: [linux]
+
+
'@rollup/rollup-linux-arm-musleabihf@4.34.8':
+
resolution: {integrity: sha512-S0lqKLfTm5u+QTxlFiAnb2J/2dgQqRy/XvziPtDd1rKZFXHTyYLoVL58M/XFwDI01AQCDIevGLbQrMAtdyanpA==}
+
cpu: [arm]
+
os: [linux]
+
+
'@rollup/rollup-linux-arm64-gnu@4.34.8':
+
resolution: {integrity: sha512-jpz9YOuPiSkL4G4pqKrus0pn9aYwpImGkosRKwNi+sJSkz+WU3anZe6hi73StLOQdfXYXC7hUfsQlTnjMd3s1A==}
+
cpu: [arm64]
+
os: [linux]
+
+
'@rollup/rollup-linux-arm64-musl@4.34.8':
+
resolution: {integrity: sha512-KdSfaROOUJXgTVxJNAZ3KwkRc5nggDk+06P6lgi1HLv1hskgvxHUKZ4xtwHkVYJ1Rep4GNo+uEfycCRRxht7+Q==}
+
cpu: [arm64]
+
os: [linux]
+
+
'@rollup/rollup-linux-loongarch64-gnu@4.34.8':
+
resolution: {integrity: sha512-NyF4gcxwkMFRjgXBM6g2lkT58OWztZvw5KkV2K0qqSnUEqCVcqdh2jN4gQrTn/YUpAcNKyFHfoOZEer9nwo6uQ==}
+
cpu: [loong64]
+
os: [linux]
+
+
'@rollup/rollup-linux-powerpc64le-gnu@4.34.8':
+
resolution: {integrity: sha512-LMJc999GkhGvktHU85zNTDImZVUCJ1z/MbAJTnviiWmmjyckP5aQsHtcujMjpNdMZPT2rQEDBlJfubhs3jsMfw==}
+
cpu: [ppc64]
+
os: [linux]
+
+
'@rollup/rollup-linux-riscv64-gnu@4.34.8':
+
resolution: {integrity: sha512-xAQCAHPj8nJq1PI3z8CIZzXuXCstquz7cIOL73HHdXiRcKk8Ywwqtx2wrIy23EcTn4aZ2fLJNBB8d0tQENPCmw==}
+
cpu: [riscv64]
+
os: [linux]
+
+
'@rollup/rollup-linux-s390x-gnu@4.34.8':
+
resolution: {integrity: sha512-DdePVk1NDEuc3fOe3dPPTb+rjMtuFw89gw6gVWxQFAuEqqSdDKnrwzZHrUYdac7A7dXl9Q2Vflxpme15gUWQFA==}
+
cpu: [s390x]
+
os: [linux]
+
+
'@rollup/rollup-linux-x64-gnu@4.34.8':
+
resolution: {integrity: sha512-8y7ED8gjxITUltTUEJLQdgpbPh1sUQ0kMTmufRF/Ns5tI9TNMNlhWtmPKKHCU0SilX+3MJkZ0zERYYGIVBYHIA==}
+
cpu: [x64]
+
os: [linux]
+
+
'@rollup/rollup-linux-x64-musl@4.34.8':
+
resolution: {integrity: sha512-SCXcP0ZpGFIe7Ge+McxY5zKxiEI5ra+GT3QRxL0pMMtxPfpyLAKleZODi1zdRHkz5/BhueUrYtYVgubqe9JBNQ==}
+
cpu: [x64]
+
os: [linux]
+
+
'@rollup/rollup-win32-arm64-msvc@4.34.8':
+
resolution: {integrity: sha512-YHYsgzZgFJzTRbth4h7Or0m5O74Yda+hLin0irAIobkLQFRQd1qWmnoVfwmKm9TXIZVAD0nZ+GEb2ICicLyCnQ==}
+
cpu: [arm64]
+
os: [win32]
+
+
'@rollup/rollup-win32-ia32-msvc@4.34.8':
+
resolution: {integrity: sha512-r3NRQrXkHr4uWy5TOjTpTYojR9XmF0j/RYgKCef+Ag46FWUTltm5ziticv8LdNsDMehjJ543x/+TJAek/xBA2w==}
+
cpu: [ia32]
+
os: [win32]
+
+
'@rollup/rollup-win32-x64-msvc@4.34.8':
+
resolution: {integrity: sha512-U0FaE5O1BCpZSeE6gBl3c5ObhePQSfk9vDRToMmTkbhCOgW4jqvtS5LGyQ76L1fH8sM0keRp4uDTsbjiUyjk0g==}
+
cpu: [x64]
+
os: [win32]
+
+
'@types/estree@1.0.6':
+
resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
+
+
esbuild@0.25.0:
+
resolution: {integrity: sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==}
+
engines: {node: '>=18'}
+
hasBin: true
+
+
fsevents@2.3.3:
+
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+
os: [darwin]
+
+
nanoid@3.3.8:
+
resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==}
+
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+
hasBin: true
+
+
picocolors@1.1.1:
+
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+
postcss@8.5.3:
+
resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==}
+
engines: {node: ^10 || ^12 || >=14}
+
+
rollup@4.34.8:
+
resolution: {integrity: sha512-489gTVMzAYdiZHFVA/ig/iYFllCcWFHMvUHI1rpFmkoUtRlQxqh6/yiNqnYibjMZ2b/+FUQwldG+aLsEt6bglQ==}
+
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+
hasBin: true
+
+
source-map-js@1.2.1:
+
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+
engines: {node: '>=0.10.0'}
+
+
typescript@5.7.3:
+
resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==}
+
engines: {node: '>=14.17'}
+
hasBin: true
+
+
vite@6.2.0:
+
resolution: {integrity: sha512-7dPxoo+WsT/64rDcwoOjk76XHj+TqNTIvHKcuMQ1k4/SeHDaQt5GFAeLYzrimZrMpn/O6DtdI03WUjdxuPM0oQ==}
+
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
+
hasBin: true
+
peerDependencies:
+
'@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
+
jiti: '>=1.21.0'
+
less: '*'
+
lightningcss: ^1.21.0
+
sass: '*'
+
sass-embedded: '*'
+
stylus: '*'
+
sugarss: '*'
+
terser: ^5.16.0
+
tsx: ^4.8.1
+
yaml: ^2.4.2
+
peerDependenciesMeta:
+
'@types/node':
+
optional: true
+
jiti:
+
optional: true
+
less:
+
optional: true
+
lightningcss:
+
optional: true
+
sass:
+
optional: true
+
sass-embedded:
+
optional: true
+
stylus:
+
optional: true
+
sugarss:
+
optional: true
+
terser:
+
optional: true
+
tsx:
+
optional: true
+
yaml:
+
optional: true
+
+
snapshots:
+
+
'@esbuild/aix-ppc64@0.25.0':
+
optional: true
+
+
'@esbuild/android-arm64@0.25.0':
+
optional: true
+
+
'@esbuild/android-arm@0.25.0':
+
optional: true
+
+
'@esbuild/android-x64@0.25.0':
+
optional: true
+
+
'@esbuild/darwin-arm64@0.25.0':
+
optional: true
+
+
'@esbuild/darwin-x64@0.25.0':
+
optional: true
+
+
'@esbuild/freebsd-arm64@0.25.0':
+
optional: true
+
+
'@esbuild/freebsd-x64@0.25.0':
+
optional: true
+
+
'@esbuild/linux-arm64@0.25.0':
+
optional: true
+
+
'@esbuild/linux-arm@0.25.0':
+
optional: true
+
+
'@esbuild/linux-ia32@0.25.0':
+
optional: true
+
+
'@esbuild/linux-loong64@0.25.0':
+
optional: true
+
+
'@esbuild/linux-mips64el@0.25.0':
+
optional: true
+
+
'@esbuild/linux-ppc64@0.25.0':
+
optional: true
+
+
'@esbuild/linux-riscv64@0.25.0':
+
optional: true
+
+
'@esbuild/linux-s390x@0.25.0':
+
optional: true
+
+
'@esbuild/linux-x64@0.25.0':
+
optional: true
+
+
'@esbuild/netbsd-arm64@0.25.0':
+
optional: true
+
+
'@esbuild/netbsd-x64@0.25.0':
+
optional: true
+
+
'@esbuild/openbsd-arm64@0.25.0':
+
optional: true
+
+
'@esbuild/openbsd-x64@0.25.0':
+
optional: true
+
+
'@esbuild/sunos-x64@0.25.0':
+
optional: true
+
+
'@esbuild/win32-arm64@0.25.0':
+
optional: true
+
+
'@esbuild/win32-ia32@0.25.0':
+
optional: true
+
+
'@esbuild/win32-x64@0.25.0':
+
optional: true
+
+
'@rollup/rollup-android-arm-eabi@4.34.8':
+
optional: true
+
+
'@rollup/rollup-android-arm64@4.34.8':
+
optional: true
+
+
'@rollup/rollup-darwin-arm64@4.34.8':
+
optional: true
+
+
'@rollup/rollup-darwin-x64@4.34.8':
+
optional: true
+
+
'@rollup/rollup-freebsd-arm64@4.34.8':
+
optional: true
+
+
'@rollup/rollup-freebsd-x64@4.34.8':
+
optional: true
+
+
'@rollup/rollup-linux-arm-gnueabihf@4.34.8':
+
optional: true
+
+
'@rollup/rollup-linux-arm-musleabihf@4.34.8':
+
optional: true
+
+
'@rollup/rollup-linux-arm64-gnu@4.34.8':
+
optional: true
+
+
'@rollup/rollup-linux-arm64-musl@4.34.8':
+
optional: true
+
+
'@rollup/rollup-linux-loongarch64-gnu@4.34.8':
+
optional: true
+
+
'@rollup/rollup-linux-powerpc64le-gnu@4.34.8':
+
optional: true
+
+
'@rollup/rollup-linux-riscv64-gnu@4.34.8':
+
optional: true
+
+
'@rollup/rollup-linux-s390x-gnu@4.34.8':
+
optional: true
+
+
'@rollup/rollup-linux-x64-gnu@4.34.8':
+
optional: true
+
+
'@rollup/rollup-linux-x64-musl@4.34.8':
+
optional: true
+
+
'@rollup/rollup-win32-arm64-msvc@4.34.8':
+
optional: true
+
+
'@rollup/rollup-win32-ia32-msvc@4.34.8':
+
optional: true
+
+
'@rollup/rollup-win32-x64-msvc@4.34.8':
+
optional: true
+
+
'@types/estree@1.0.6': {}
+
+
esbuild@0.25.0:
+
optionalDependencies:
+
'@esbuild/aix-ppc64': 0.25.0
+
'@esbuild/android-arm': 0.25.0
+
'@esbuild/android-arm64': 0.25.0
+
'@esbuild/android-x64': 0.25.0
+
'@esbuild/darwin-arm64': 0.25.0
+
'@esbuild/darwin-x64': 0.25.0
+
'@esbuild/freebsd-arm64': 0.25.0
+
'@esbuild/freebsd-x64': 0.25.0
+
'@esbuild/linux-arm': 0.25.0
+
'@esbuild/linux-arm64': 0.25.0
+
'@esbuild/linux-ia32': 0.25.0
+
'@esbuild/linux-loong64': 0.25.0
+
'@esbuild/linux-mips64el': 0.25.0
+
'@esbuild/linux-ppc64': 0.25.0
+
'@esbuild/linux-riscv64': 0.25.0
+
'@esbuild/linux-s390x': 0.25.0
+
'@esbuild/linux-x64': 0.25.0
+
'@esbuild/netbsd-arm64': 0.25.0
+
'@esbuild/netbsd-x64': 0.25.0
+
'@esbuild/openbsd-arm64': 0.25.0
+
'@esbuild/openbsd-x64': 0.25.0
+
'@esbuild/sunos-x64': 0.25.0
+
'@esbuild/win32-arm64': 0.25.0
+
'@esbuild/win32-ia32': 0.25.0
+
'@esbuild/win32-x64': 0.25.0
+
+
fsevents@2.3.3:
+
optional: true
+
+
nanoid@3.3.8: {}
+
+
picocolors@1.1.1: {}
+
+
postcss@8.5.3:
+
dependencies:
+
nanoid: 3.3.8
+
picocolors: 1.1.1
+
source-map-js: 1.2.1
+
+
rollup@4.34.8:
+
dependencies:
+
'@types/estree': 1.0.6
+
optionalDependencies:
+
'@rollup/rollup-android-arm-eabi': 4.34.8
+
'@rollup/rollup-android-arm64': 4.34.8
+
'@rollup/rollup-darwin-arm64': 4.34.8
+
'@rollup/rollup-darwin-x64': 4.34.8
+
'@rollup/rollup-freebsd-arm64': 4.34.8
+
'@rollup/rollup-freebsd-x64': 4.34.8
+
'@rollup/rollup-linux-arm-gnueabihf': 4.34.8
+
'@rollup/rollup-linux-arm-musleabihf': 4.34.8
+
'@rollup/rollup-linux-arm64-gnu': 4.34.8
+
'@rollup/rollup-linux-arm64-musl': 4.34.8
+
'@rollup/rollup-linux-loongarch64-gnu': 4.34.8
+
'@rollup/rollup-linux-powerpc64le-gnu': 4.34.8
+
'@rollup/rollup-linux-riscv64-gnu': 4.34.8
+
'@rollup/rollup-linux-s390x-gnu': 4.34.8
+
'@rollup/rollup-linux-x64-gnu': 4.34.8
+
'@rollup/rollup-linux-x64-musl': 4.34.8
+
'@rollup/rollup-win32-arm64-msvc': 4.34.8
+
'@rollup/rollup-win32-ia32-msvc': 4.34.8
+
'@rollup/rollup-win32-x64-msvc': 4.34.8
+
fsevents: 2.3.3
+
+
source-map-js@1.2.1: {}
+
+
typescript@5.7.3: {}
+
+
vite@6.2.0:
+
dependencies:
+
esbuild: 0.25.0
+
postcss: 8.5.3
+
rollup: 4.34.8
+
optionalDependencies:
+
fsevents: 2.3.3
+324
src/frag-canvas-element.ts
···
+
const VS_SOURCE_100 =
+
'attribute vec2 vPos;\n'
+
+ 'void main() {\n'
+
+ ' gl_Position = vec4(vPos, 0.0, 1.0);\n'
+
+ '}';
+
const VS_SOURCE_300 =
+
'#version 300 es\n'
+
+ 'in vec4 vPos;\n'
+
+ 'void main() {\n'
+
+ ' gl_Position = vPos;\n'
+
+ '}';
+
+
const makeDateVector = () => {
+
const DATE = new Date();
+
const year = DATE.getFullYear();
+
const month = DATE.getMonth() + 1;
+
const day = DATE.getDate();
+
const time = DATE.getHours() * 60 * 60 + DATE.getMinutes() * 60 + DATE.getSeconds() + DATE.getMilliseconds() * 0.001;
+
return [year, month, day, time] as const;
+
};
+
+
interface InitState {
+
width: number;
+
height: number;
+
fragSource: string;
+
}
+
+
function createState(gl: WebGL2RenderingContext, init: InitState) {
+
const program = gl.createProgram();
+
+
const vertShader300 = gl.createShader(gl.VERTEX_SHADER);
+
const vertShader100 = gl.createShader(gl.VERTEX_SHADER);
+
+
const fragShader = gl.createShader(gl.FRAGMENT_SHADER);
+
if (!vertShader100 || !vertShader300 || !fragShader) {
+
return null;
+
}
+
+
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
+
+
gl.shaderSource(vertShader100, VS_SOURCE_100);
+
gl.compileShader(vertShader100);
+
gl.shaderSource(vertShader300, VS_SOURCE_300);
+
gl.compileShader(vertShader300);
+
+
const screenVertex = new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]);
+
const vertexBuffer = gl.createBuffer();
+
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
+
gl.bufferData(gl.ARRAY_BUFFER, screenVertex, gl.STATIC_DRAW);
+
+
const texture = gl.createTexture();
+
gl.activeTexture(gl['TEXTURE0']);
+
gl.bindTexture(gl.TEXTURE_2D, texture);
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+
+
let width = init.width;
+
let height = init.height;
+
+
let vertexPos: GLint = 0;
+
let iResolution: WebGLUniformLocation | null = null;
+
let iChannelResolution: WebGLUniformLocation | null = null;
+
let iTime: WebGLUniformLocation | null = null;
+
let iTimeDelta: WebGLUniformLocation | null = null;
+
let iFrame: WebGLUniformLocation | null = null;
+
let iChannel: WebGLUniformLocation | null = null;
+
let iDate: WebGLUniformLocation | null = null;
+
+
let frameCount = 0;
+
let prevTimestamp: DOMHighResTimeStamp;
+
+
const state = {
+
draw(source: TexImageSource, timestamp: DOMHighResTimeStamp) {
+
prevTimestamp = timestamp;
+
+
gl.useProgram(program);
+
+
if (source) {
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
+
if (iChannelResolution)
+
gl.uniform3fv(iChannelResolution, [width, height, 0]);
+
} else {
+
if (iChannelResolution)
+
gl.uniform3fv(iChannelResolution, [0, 0, 0]);
+
}
+
+
if (iResolution)
+
gl.uniform2f(iResolution, width, height);
+
if (iTime)
+
gl.uniform1f(iTime, timestamp / 1000);
+
if (iTimeDelta)
+
gl.uniform1f(iTime, (timestamp - prevTimestamp) / 1000);
+
if (iFrame)
+
gl.uniform1f(iFrame, frameCount++);
+
if (iChannel)
+
gl.uniform1i(iChannel, 0);
+
if (iDate)
+
gl.uniform4f(iDate, ...makeDateVector());
+
+
gl.enableVertexAttribArray(vertexPos);
+
gl.vertexAttribPointer(vertexPos, 2, gl.FLOAT, false, 0, 0);
+
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
+
},
+
+
updateViewport(newWidth: number, newHeight: number) {
+
gl.canvas.width = (width = newWidth);
+
gl.canvas.height = (height = newHeight);
+
gl.viewport(0, 0, width, height);
+
},
+
+
updateFragShader(fragSource: string) {
+
fragSource = fragSource.trim();
+
gl.shaderSource(fragShader, fragSource);
+
gl.compileShader(fragShader);
+
+
const vertShader = /\s+#version 300/i.test(fragSource) ? vertShader300 : vertShader100;
+
gl.attachShader(program, vertShader);
+
gl.attachShader(program, fragShader);
+
+
gl.linkProgram(program);
+
+
vertexPos = gl.getAttribLocation(program, 'vPos');
+
iResolution = gl.getUniformLocation(program, 'iResolution');
+
iChannelResolution = gl.getUniformLocation(program, 'iChannelResolution');
+
iTime = gl.getUniformLocation(program, 'iTime');
+
iTimeDelta = gl.getUniformLocation(program, 'iTimeDelta');
+
iFrame = gl.getUniformLocation(program, 'iFrame');
+
iChannel = gl.getUniformLocation(program, 'iChannel');
+
iDate = gl.getUniformLocation(program, 'iDate');
+
},
+
+
drawImmediate() {
+
gl.useProgram(program);
+
gl.enableVertexAttribArray(vertexPos);
+
gl.vertexAttribPointer(vertexPos, 2, gl.FLOAT, false, 0, 0);
+
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
+
},
+
};
+
+
state.updateViewport(width, height);
+
state.updateFragShader(init.fragSource);
+
return state;
+
}
+
+
class FragCanvas extends HTMLElement implements HTMLCanvasElement {
+
static observedAttributes = [];
+
+
private state: ReturnType<typeof createState> | null;
+
private input: HTMLCanvasElement | HTMLImageElement | HTMLVideoElement;
+
private output: HTMLCanvasElement;
+
+
#mutationObserver = new MutationObserver(() => {
+
if (this.state) {
+
this.state.updateFragShader(this.source);
+
}
+
});
+
+
#resizeObserver = new ResizeObserver((entries) => {
+
const entry = entries[0];
+
if (this.state && entry) {
+
const width = entry.devicePixelContentBoxSize[0].inlineSize;
+
const height = entry.devicePixelContentBoxSize[0].blockSize;
+
if (this.autoresize) {
+
this.input.width = width;
+
this.input.height = height;
+
}
+
this.state.updateViewport(width, height);
+
this.state.drawImmediate();
+
this.#rescheduleDraw();
+
}
+
});
+
+
constructor() {
+
super();
+
+
const sheet = new CSSStyleSheet();
+
sheet.insertRule(':host([hidden]) { display: none; }');
+
sheet.insertRule(':host { display: block; position: relative; }');
+
sheet.insertRule(':host * { position: absolute; width: 100%; height: 100%; }');
+
sheet.insertRule(':host *:not(:last-child) { visibility: hidden; }');
+
+
const shadow = this.attachShadow({ mode: 'closed' });
+
const output = (this.output = document.createElement('canvas'));
+
const input = (this.input = (this.querySelector(':not(canvas, script)') || document.createElement('canvas')));
+
+
shadow.adoptedStyleSheets = [sheet];
+
shadow.appendChild(input);
+
shadow.appendChild(output);
+
}
+
+
getContext(contextId: '2d', options?: CanvasRenderingContext2DSettings): CanvasRenderingContext2D | null;
+
getContext(contextId: 'bitmaprenderer', options?: ImageBitmapRenderingContextSettings): ImageBitmapRenderingContext | null;
+
getContext(contextId: 'webgl', options?: WebGLContextAttributes): WebGLRenderingContext | null;
+
getContext(contextId: 'webgl2', options?: WebGLContextAttributes): WebGL2RenderingContext | null;
+
+
getContext(contextId: string, options?: any) {
+
if (!(this.input instanceof HTMLCanvasElement)) {
+
return null;
+
}
+
this.input.width = this.width;
+
this.input.height = this.height;
+
return this.input.getContext(contextId, {
+
alpha: true,
+
desynchronized: true,
+
preserveDrawingBuffer: true,
+
...options,
+
});
+
}
+
+
toBlob(callback: BlobCallback, type?: string, quality?: any): void {
+
return this.output.toBlob(callback, type, quality);
+
}
+
+
toDataURL(type?: string, quality?: any): string {
+
return this.output.toDataURL(type, quality);
+
}
+
+
captureStream(frameRequestRate?: number): MediaStream {
+
return this.output.captureStream(frameRequestRate);
+
}
+
+
transferControlToOffscreen(): OffscreenCanvas {
+
return (this.input instanceof HTMLCanvasElement ? this.input : this.output).transferControlToOffscreen();
+
}
+
+
get autoresize() {
+
return this.hasAttribute('autoresize');
+
}
+
+
set autoresize(autoresize: boolean) {
+
if (autoresize) {
+
this.setAttribute('autoresize', '');
+
} else {
+
this.removeAttribute('autoresize');
+
}
+
}
+
+
get source() {
+
let text = '';
+
for (const child of this.childNodes) {
+
if (child.nodeType === Node.TEXT_NODE) {
+
text += child.textContent || '';
+
} else if (child instanceof HTMLScriptElement) {
+
text = child.textContent || '';
+
break;
+
}
+
}
+
return text.trim();
+
}
+
+
get width() {
+
if (this.state) {
+
return this.output.width;
+
} else {
+
return this.clientWidth * devicePixelRatio;
+
}
+
}
+
+
set width(width) {
+
this.input.width = width;
+
}
+
+
get height() {
+
if (this.state) {
+
return this.output.height;
+
} else {
+
return this.clientHeight * devicePixelRatio;
+
}
+
}
+
+
set height(height) {
+
this.input.height = height;
+
}
+
+
#frameID: number | undefined;
+
#rescheduleDraw() {
+
const self = this;
+
if (this.#frameID !== undefined) {
+
cancelAnimationFrame(this.#frameID);
+
this.#frameID = undefined;
+
}
+
this.#frameID = requestAnimationFrame(function draw(timestamp: DOMHighResTimeStamp) {
+
if (self.state) {
+
self.state.draw(self.input, timestamp);
+
self.#frameID = requestAnimationFrame(draw);
+
}
+
});
+
}
+
+
connectedCallback() {
+
const gl = this.output.getContext('webgl2', {
+
alpha: true,
+
desynchronized: true,
+
preserveDrawingBuffer: true,
+
});
+
+
const init = {
+
fragSource: this.source,
+
width: this.clientWidth * devicePixelRatio,
+
height: this.clientHeight * devicePixelRatio,
+
};
+
+
const state = (this.state = gl && createState(gl, init));
+
if (state) {
+
this.#mutationObserver.observe(this, { subtree: true, characterData: true });
+
this.#resizeObserver.observe(this, { box: 'device-pixel-content-box' });
+
this.#rescheduleDraw();
+
}
+
}
+
+
disconnectedCallback() {
+
this.#mutationObserver.disconnect();
+
this.#resizeObserver.disconnect();
+
if (this.#frameID !== undefined) {
+
cancelAnimationFrame(this.#frameID);
+
this.#frameID = undefined;
+
}
+
}
+
}
+
+
customElements.define('frag-canvas', FragCanvas);
+
export { FragCanvas };
+1
src/index.ts
···
+
export * from './frag-canvas-element';