
Security News
Socket Releases Free Certified Patches for Critical vm2 Sandbox Escape
A critical vm2 sandbox escape can allow untrusted JavaScript to break isolation and execute commands on the host Node.js process.
Render GLTF files to PNG images in completely native JavaScript without WebGL/OpenGL.
Render GLTF files to PNG images in completely native JavaScript without WebGL/OpenGL.
Give poppygl a .gltf or .glb URL and it will fetch every referenced buffer/texture, rasterize the scene, and hand back a PNG buffer.
import { renderGLTFToPNGBufferFromURL } from "poppygl"
import { writeFile } from "node:fs/promises"
const png = await renderGLTFToPNGBufferFromURL(
"https://models.babylonjs.com/DamagedHelmet.glb",
{
width: 800,
height: 600,
ambient: 0.15,
},
)
await writeFile("DamagedHelmet.png", png)
The fetch helper relies on the global
fetchAPI (present in Node 18+ and modern browsers). Pass a customfetchImplif you need different transport or caching behaviour.
Already have the .glb bytes (for example, uploaded by a user or read from disk)? Send the buffer straight to renderGLTFToPNGBufferFromGLBBuffer and receive the PNG output.
import { readFile } from "node:fs/promises"
import { renderGLTFToPNGBufferFromGLBBuffer } from "poppygl"
const glb = await readFile("DamagedHelmet.glb")
const png = await renderGLTFToPNGBufferFromGLBBuffer(glb, {
width: 1024,
height: 768,
})
// write or return the PNG buffer
This helper expects every referenced buffer/image to be embedded in the GLB (the usual single-file package). If the asset links out to external resources, load it with
renderGLTFToPNGBufferFromURLinstead so poppygl can fetch the extras.
renderGLTFToPNGBufferFromURL accepts the same render options as the lower-level APIs:
width/height (default 512): output resolution in pixels.supersampling: render at width * supersampling / height * supersampling, then downsample (default 1).fov: vertical field of view in degrees (defaults to 35).camPos and lookAt: override the auto-framed camera position and target.up: choose the world-up axis for the camera with "y+" | "y-" | "x+" | "x-" | "z+" | "z-".cameraRotation: apply Euler degrees { x, y, z } to define camera orientation directly. When set, it takes precedence over lookAt.debugPoints: optional { label, position }[] overlay rendered on top of the final PNG for world-space debugging markers.debugFontSize: optional pixel size for debugPoints labels; when omitted the renderer derives a size from the image dimensions.debugPointColor: optional RGB tuple for debug point markers.debugLabelColor: optional RGB tuple for debug point labels.lightDir: normalized directional light vector (defaults to a top-right key light).ambient: ambient lighting contribution (0–1, defaults to 0.2).gamma: gamma correction applied to the output (defaults to 2.2).cull: back-face culling mode ("back", "front", or "none").fetchImpl: optional override for resource loading (must match the fetch signature).You can inspect the defaults via getDefaultRenderOptions() or reuse the internal merge logic with resolveRenderOptions().
When the GLTF JSON object is already in memory (for example, bundled with your app), skip the network loader and supply resources directly:
import {
bufferFromDataURI,
createSceneFromGLTF,
decodeImageFromBuffer,
encodePNGToBuffer,
pureImageFactory,
renderSceneFromGLTF,
} from "poppygl"
import gltfJson from "./CesiumMan.gltf.json" assert { type: "json" }
import { readFile } from "node:fs/promises"
const base = new URL("./CesiumMan/", import.meta.url)
const buffers = await Promise.all(
(gltfJson.buffers ?? []).map(async (entry) => {
if (!entry.uri) throw new Error("Buffers without URIs need custom handling.")
return entry.uri.startsWith("data:")
? bufferFromDataURI(entry.uri)
: await readFile(new URL(entry.uri, base))
}),
)
const images = await Promise.all(
(gltfJson.images ?? []).map(async (img) => {
if (!img.uri) throw new Error("Only URI-backed images are shown in this example.")
const data = img.uri.startsWith("data:")
? bufferFromDataURI(img.uri)
: await readFile(new URL(img.uri, base))
return decodeImageFromBuffer(data, img.mimeType)
}),
)
const scene = createSceneFromGLTF(gltfJson, { buffers, images })
const { bitmap } = renderSceneFromGLTF(scene, { width: 512, height: 512 }, pureImageFactory)
const png = await encodePNGToBuffer(bitmap)
The only contract is that buffers is an array of Uint8Array instances and images is an array of BitmapLike textures (PNG and JPEG are supported out of the box via decodeImageFromBuffer).
loadGLTFWithResourcesFromURL returns { gltf, resources } if you prefer to inspect or cache the parsed data before rendering.createSceneFromGLTF builds draw calls ready for the software rasterizer.computeSmoothNormals and computeWorldAABB expose useful preprocessing helpers.pureImageFactory allocates the pureimage bitmap implementation used by the renderer.encodePNGToBuffer packs any BitmapLike into a PNG buffer that can be written to disk or served over the network.Happy rendering!
FAQs
Render GLTF files to PNG images in completely native JavaScript without WebGL/OpenGL.
The npm package poppygl receives a total of 10,063 weekly downloads. As such, poppygl popularity was classified as popular.
We found that poppygl demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
A critical vm2 sandbox escape can allow untrusted JavaScript to break isolation and execute commands on the host Node.js process.

Research
Five malicious NuGet packages impersonate Chinese .NET libraries to deploy a stealer targeting browser credentials, crypto wallets, SSH keys, and local files.

Security News
pnpm 11 turns on a 1-day Minimum Release Age and blocks exotic subdeps by default, adding safeguards against fast-moving supply chain attacks.