{"_id":"@arikata-nobuaki/videoframeuploader","_rev":"2-79587d478dc8b58f7c0c61a37fb299c4","name":"@arikata-nobuaki/videoframeuploader","dist-tags":{"latest":"0.1.1"},"versions":{"0.1.0":{"name":"@arikata-nobuaki/videoframeuploader","version":"0.1.0","keywords":["webgpu","three.js","video","videoframe","texture","importExternalTexture"],"author":{"name":"Nobuaki Arikata"},"license":"MIT","_id":"@arikata-nobuaki/videoframeuploader@0.1.0","maintainers":[{"name":"arikata-nobuaki","email":"n.arikata@gmail.com"}],"dist":{"shasum":"c7ed7624e119d1f814e7f208a4d150fa61b6e24f","tarball":"https://registry.npmjs.org/@arikata-nobuaki/videoframeuploader/-/videoframeuploader-0.1.0.tgz","fileCount":5,"integrity":"sha512-BrmGrtK74ySjg7TmWzyEShMQwLqAywMNJAWboe99jGaOWe0DGrOLb+B6EVdH06f5QmoxfsBLyqRu6vkFBsngWQ==","signatures":[{"sig":"MEUCIQDkJssgy5aCw4PKJv9QPwa12QJsV9wLV0taBQzgkUUq7gIgYcvPfiJjtrvqSVTiI0jf/YMvdDrPU94W89zT/kaNjdQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":31216},"type":"module","exports":{".":{"import":"./dist/videoframeuploader.js"}},"gitHead":"adb6785c0c36a229f4541d537cca8868946e4341","scripts":{"build":"vite build","prepublishOnly":"npm run build","preview:demo:minimal":"vite build && npx vite examples/minimal","preview:demo:videotexture":"vite build && npx vite examples/videotexture","preview:demo:videoframetexture":"vite build && npx vite examples/videoframetexture"},"_npmUser":{"name":"arikata-nobuaki","email":"n.arikata@gmail.com"},"_npmVersion":"11.12.1","description":"Zero-copy video textures for Three.js WebGPU via importExternalTexture","directories":{},"sideEffects":false,"_nodeVersion":"25.9.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^8.0.12","three":"^0.184.0","terser":"^5.48.0"},"peerDependencies":{"three":">=0.184.0"},"_npmOperationalInternal":{"tmp":"tmp/videoframeuploader_0.1.0_1780403836125_0.7697785579377774","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@arikata-nobuaki/videoframeuploader","version":"0.1.1","description":"Zero-copy video textures for Three.js WebGPU via importExternalTexture","license":"MIT","author":{"name":"Nobuaki Arikata"},"type":"module","sideEffects":false,"keywords":["webgpu","three.js","video","videoframe","texture","importExternalTexture"],"scripts":{"build":"vite build","prepublishOnly":"npm run build","preview:demo:minimal":"vite build && npx vite examples/minimal","preview:demo:videotexture":"vite build && npx vite examples/videotexture","preview:demo:videoframetexture":"vite build && npx vite examples/videoframetexture"},"exports":{".":{"import":"./dist/videoframeuploader.js"}},"peerDependencies":{"three":">=0.184.0"},"publishConfig":{"access":"public"},"devDependencies":{"terser":"^5.48.0","three":"^0.184.0","vite":"^8.0.12"},"gitHead":"88a2f4ec104c29963a008102d1bbc6854ce8b887","_id":"@arikata-nobuaki/videoframeuploader@0.1.1","_nodeVersion":"25.9.0","_npmVersion":"11.12.1","dist":{"integrity":"sha512-Xgw8TUL6VankZOldhdlR8YYgRUNT+0BtBBvJmH6eB8K1dg79lDdh6aKis/0e1m7t0Xhv/3ianq5hOeEzgXbPBA==","shasum":"2a00a4be7cd6dbc173c7b38afdc799ea2189665d","tarball":"https://registry.npmjs.org/@arikata-nobuaki/videoframeuploader/-/videoframeuploader-0.1.1.tgz","fileCount":5,"unpackedSize":28843,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIGV24YZ9D5emf2x0pqG2jZ6lKHf28i6CRVLyHiSFSv5eAiEA1/8i5vZJ3oJ4xwQ5hZ+iAgJ6pvMAxxa+GgX7zrTVrJM="}]},"_npmUser":{"name":"arikata-nobuaki","email":"n.arikata@gmail.com"},"directories":{},"maintainers":[{"name":"arikata-nobuaki","email":"n.arikata@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/videoframeuploader_0.1.1_1780443766917_0.896634125142582"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-02T12:37:15.929Z","modified":"2026-06-02T23:42:47.188Z","0.1.0":"2026-06-02T12:37:16.263Z","0.1.1":"2026-06-02T23:42:47.060Z"},"author":{"name":"Nobuaki Arikata"},"license":"MIT","keywords":["webgpu","three.js","video","videoframe","texture","importExternalTexture"],"description":"Zero-copy video textures for Three.js WebGPU via importExternalTexture","maintainers":[{"name":"arikata-nobuaki","email":"n.arikata@gmail.com"}],"readme":"# VideoFrameUploader\n\nZero-copy video textures for **Three.js WebGPU** using `importExternalTexture`.\n\nFeed decoded `VideoFrame` objects into a render target, display them with standard TSL materials—no Three.js fork required. Decoding (HTML `<video>`, WebCodecs, etc.) stays on your side.\n\n## Why\n\nOn Safari WebGPU, uploading video every frame via `VideoTexture` or `VideoFrameTexture` (`copyExternalImageToTexture`) is often a bottleneck. This library blits `VideoFrame` into a render target with `importExternalTexture`, then samples that RT from Three.js—matching the zero-copy path used in raw WebGPU demos.\n\n## Requirements\n\n- **WebGPU** (`navigator.gpu`)\n- **`VideoFrame`** API\n- **[Three.js](https://threejs.org/)** `>=0.184.0` with `WebGPURenderer` (`three/webgpu`)\n- Safari 18.2+ (or another browser with WebGPU + `VideoFrame` from video)\n\nNo MediaBunny or other decoder is bundled. You supply frames via `ExternalVideoSurface.setFrame()`.\n\n## Install\n\n```bash\nnpm install @arikata-nobuaki/videoframeuploader\n```\n\nOr clone this repo, build the library, and import from `dist/`:\n\n```bash\nnpm install\nnpm run build\n```\n\n## Quick start\n\n```js\nimport * as THREE from 'three/webgpu'\nimport { VideoFrameUploader, checkCapabilities } from '@arikata-nobuaki/videoframeuploader'\n\nconst caps = checkCapabilities()\nif (!caps.canRun) throw new Error('WebGPU or VideoFrame not available')\n\nconst canvas = document.querySelector('#canvas')\nconst renderer = new THREE.WebGPURenderer({ canvas, antialias: true })\nrenderer.outputColorSpace = THREE.SRGBColorSpace\nawait renderer.init()\n\nconst scene = new THREE.Scene()\nconst camera = new THREE.PerspectiveCamera(45, innerWidth / innerHeight, 0.1, 100)\ncamera.position.z = 2\n\nconst uploader = await VideoFrameUploader.create(renderer)\nconst surface = uploader.createSurface()\nscene.add(surface.mesh)\n\nconst video = document.createElement('video')\nvideo.src = '/path/to/video.mp4'\nvideo.muted = true\nvideo.loop = true\nawait video.play()\n\nrenderer.setAnimationLoop(() => {\n  if (video.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA) {\n    surface.setFrame(new VideoFrame(video))\n    surface.setRenderSize(video.videoWidth, video.videoHeight)\n  }\n\n  uploader.update({ surfaces: [surface] })\n  renderer.render(scene, camera) // or composer.render()\n})\n```\n\n## API\n\n### `VideoFrameUploader.create(renderer)`\n\nCreates an uploader from **your** `WebGPURenderer`. Calls `renderer.init()` if needed.\n\n### `uploader.createSurface({ buildColorNode? })`\n\nReturns an `ExternalVideoSurface` with:\n\n| Member | Description |\n|--------|-------------|\n| `mesh` | `THREE.Mesh` — add to your scene and position/scale |\n| `setFrame(frame)` | Set a `VideoFrame` (previous frame is closed) |\n| `setRenderSize(w, h)` | RT size in pixels |\n| `texture` / `renderTarget` | Read-only access to the blit target |\n| `dispose()` | Release surface resources |\n\nOptional `buildColorNode` customizes the TSL color node (default applies `colorSpaceToWorking` for correct sRGB).\n\n### `uploader.update({ surfaces })`\n\nBlits each surface’s `VideoFrame` into its render target. **Does not call `renderer.render()`**—you control the final draw (plain render, `EffectComposer`, etc.).\n\n### `uploader.dispose()`\n\nReleases internal blit pipeline only. You dispose the renderer and surfaces.\n\n### `checkCapabilities()`\n\nReturns `{ hasWebGPU, hasVideoFrame, canRun }`.\n\n## Build\n\n```bash\nnpm run build\n```\n\nOutput: `dist/videoframeuploader.js` (minified ESM, `three` externalized as peer dependency).\n\n## Examples\n\nPlace a sample MP4 at `examples/common/video/seaside.mp4` (or change `VIDEO_FILE` in `examples/common/js/constants.js`).\n\n| Demo | Path | Description |\n|------|------|-------------|\n| Zero-copy (this library) | `examples/minimal` | `VideoFrame` → `VideoFrameUploader` |\n| Copy baseline | `examples/videotexture` | `<video>` → `VideoTexture` |\n| Copy baseline | `examples/videoframetexture` | `VideoFrame` → `VideoFrameTexture` |\n\nAll demos use a 2×2 grid with four independent `<video>` elements for fair FPS comparison.\n\n```bash\nnpm run build\nnpm run preview:demo:minimal\nnpm run preview:demo:videotexture\nnpm run preview:demo:videoframetexture\n```\n\n## Project layout\n\n```\nsrc/                 Library source\ndist/                Built bundle (after npm run build)\nexamples/            Comparison demos + shared common/\nLICENSE              MIT\n```\n\n## License\n\nMIT © [Nobuaki Arikata](./LICENSE)\n","readmeFilename":"README.md"}