{"id":14525191,"url":"https://github.com/akira-cn/node-canvas-webgl","last_synced_at":"2025-05-12T13:27:28.220Z","repository":{"id":48535460,"uuid":"223892145","full_name":"akira-cn/node-canvas-webgl","owner":"akira-cn","description":"Integration of node-canvas and headless-gl.","archived":false,"fork":false,"pushed_at":"2024-08-30T01:23:46.000Z","size":770,"stargazers_count":105,"open_issues_count":8,"forks_count":27,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-31T22:34:50.311Z","etag":null,"topics":["canvas","node-canvas","node-webgl","nodejs","webgl"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/akira-cn.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-11-25T07:56:17.000Z","updated_at":"2025-03-04T14:29:28.000Z","dependencies_parsed_at":"2024-12-16T05:06:22.154Z","dependency_job_id":null,"html_url":"https://github.com/akira-cn/node-canvas-webgl","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":"spritejs/sprite-template","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/akira-cn%2Fnode-canvas-webgl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/akira-cn%2Fnode-canvas-webgl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/akira-cn%2Fnode-canvas-webgl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/akira-cn%2Fnode-canvas-webgl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/akira-cn","download_url":"https://codeload.github.com/akira-cn/node-canvas-webgl/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253747131,"owners_count":21957698,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["canvas","node-canvas","node-webgl","nodejs","webgl"],"created_at":"2024-09-04T14:00:59.021Z","updated_at":"2025-05-12T13:27:28.198Z","avatar_url":"https://github.com/akira-cn.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# node-canvas-webgl\n\nIntegration of [node-canvas](https://github.com/Automattic/node-canvas) and [headless-gl](https://github.com/stackgl/headless-gl).\n\n## Demos:\n\n#### with threejs\n\n```js\nconst fs = require('fs');\nconst THREE = require('three');\nconst GIFEncoder = require('gifencoder');\nconst {createCanvas} = require('../lib');\n\nconst width = 512,\n  height = 512;\n\nconst scene = new THREE.Scene();\nconst camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);\n\nconst canvas = createCanvas(width, height);\n\nconst renderer = new THREE.WebGLRenderer({\n  canvas,\n});\n\nconst geometry = new THREE.BoxGeometry(1, 1, 1);\nconst material = new THREE.MeshBasicMaterial({color: 0x00ff00});\nconst cube = new THREE.Mesh(geometry, material);\nscene.add(cube);\n\ncamera.position.z = 5;\n\nconst encoder = new GIFEncoder(width, height);\nencoder.createReadStream().pipe(fs.createWriteStream('./snapshot/threejs-cube.gif'));\nencoder.start();\nencoder.setRepeat(0); // 0 for repeat, -1 for no-repeat\nencoder.setDelay(16); // frame delay in ms\nencoder.setQuality(10); // image quality. 10 is default.\n\nlet idx = 0;\nfunction update() {\n  cube.rotation.x += 0.01;\n  cube.rotation.y += 0.01;\n  renderer.render(scene, camera);\n  if(idx \u003e 0) {\n    encoder.addFrame(canvas.__ctx__);\n    console.log(`add frame ${idx}`);\n  }\n  idx++;\n  if(idx \u003c 500) {\n    setTimeout(update, 16);\n  }\n}\nupdate();\n```\n\n![](https://p2.ssl.qhimg.com/t01abe0b6dc18945e43.gif)\n\n#### with claygl\n\n```js\nconst fs = require('fs');\nconst GIFEncoder = require('gifencoder');\nconst clay = require('claygl');\nconst {createCanvas} = require('../lib');\n\nconst width = 512,\n  height = 512;\n\nconst canvas = createCanvas(width, height);\n\nconst encoder = new GIFEncoder(width, height);\n\nlet idx = 0;\n\n// polyfill\nglobal.document = {\n  createElement() {\n    return createCanvas(300, 150);\n  },\n};\n\nclay.application.create(canvas, {\n\n  width,\n  height,\n\n  init(app) {\n    // Create camera\n    this._camera = app.createCamera([0, 2, 5], [0, 0, 0]);\n\n    // Create a RED cube\n    this._cube = app.createCube({\n      color: [1, 0, 0],\n    });\n\n    // Create light\n    this._mainLight = app.createDirectionalLight([-1, -1, -1]);\n    // app.createAmbientLight('#fff', 1.0);\n\n    encoder.createReadStream().pipe(fs.createWriteStream('./snapshot/claygl-cube.gif'));\n    encoder.start();\n    encoder.setRepeat(0); // 0 for repeat, -1 for no-repeat\n    encoder.setDelay(16); // frame delay in ms\n    encoder.setQuality(10); // image quality. 10 is default.\n  },\n  loop(app) {\n    this._cube.rotation.rotateY(16 / 1000);\n    if(idx \u003e 0) {\n      encoder.addFrame(canvas.__ctx__);\n      console.log(`add frame ${idx}`);\n    }\n    idx++;\n    if(idx \u003e 197) {\n      process.exit(-1);\n    }\n  },\n});\n```\n\n![](https://p3.ssl.qhimg.com/t011d79d79adb1b649e.gif)\n\n#### with babylonjs\n\n```js\nconst fs = require('fs');\nconst BABYLON = require('babylonjs');\nconst {createCanvas} = require('../lib');\n\n// polyfill\nglobal.HTMLElement = function () {};\nglobal.window = {\n  setTimeout,\n  addEventListener() {},\n};\nglobal.navigator = {};\nglobal.document = {\n  createElement() {\n    return createCanvas(300, 150);\n  },\n  addEventListener() {},\n};\n\n// Get the canvas DOM element\nconst canvas = createCanvas(512, 512);\n\n// Load the 3D engine\nconst engine = new BABYLON.Engine(canvas, true, {preserveDrawingBuffer: true, stencil: true});\n\n// CreateScene function that creates and return the scene\nconst createScene = function () {\n  // Create a basic BJS Scene object\n  const scene = new BABYLON.Scene(engine);\n  // Create a FreeCamera, and set its position to {x: 0, y: 5, z: -10}\n  const camera = new BABYLON.FreeCamera('camera1', new BABYLON.Vector3(0, 5, -10), scene);\n  // Target the camera to scene origin\n  camera.setTarget(BABYLON.Vector3.Zero());\n  // Attach the camera to the canvas\n  camera.attachControl(canvas, false);\n  // Create a basic light, aiming 0, 1, 0 - meaning, to the sky\n  const light = new BABYLON.HemisphericLight('light1', new BABYLON.Vector3(0, 1, 0), scene);\n  // Create a built-in \"sphere\" shape; its constructor takes 6 params: name, segment, diameter, scene, updatable, sideOrientation\n  const sphere = BABYLON.Mesh.CreateSphere('sphere1', 16, 2, scene, false, BABYLON.Mesh.FRONTSIDE);\n  // Move the sphere upward 1/2 of its height\n  sphere.position.y = 1;\n  // Create a built-in \"ground\" shape; its constructor takes 6 params : name, width, height, subdivision, scene, updatable\n  const ground = BABYLON.Mesh.CreateGround('ground1', 6, 6, 2, scene, false);\n  // Return the created scene\n  return scene;\n};\n\n// call the createScene function\nconst scene = createScene();\n\nscene.render();\n\nfs.writeFileSync('./snapshot/snap-babylon.png', canvas.toBuffer());\n```\n\n![](https://p5.ssl.qhimg.com/t01d9af9c9428b18585.png)\n\n#### with mesh.js\n\n```js\nconst fs = require('fs');\nconst {Renderer, Figure2D, Mesh2D} = require('@mesh.js/core');\nconst {createCanvas, loadImage} = require('../lib');\n\nconst width = 512,\n  height = 512;\n\nconst canvas = createCanvas(width, height);\n\nconst renderer = new Renderer(canvas, {contextType: 'webgl'});\n\nconst f = new Figure2D();\nf.rect(0, 0, 512, 512);\n\nconst m = new Mesh2D(f, canvas);\nm.setFill({color: '#ddd'});\n\nconst url = 'https://p0.ssl.qhimg.com/t01a72262146b87165f.png';\n\nloadImage(url).then((image) =\u003e {\n  const texture = renderer.createTexture(image);\n  m.setTexture(texture);\n  renderer.drawMeshes([m]);\n  fs.writeFileSync('./snapshot/snap-spritejs.png', canvas.toBuffer());\n});\n```\n\n![](https://p2.ssl.qhimg.com/t01c3b877430c190327.png)\n\n#### with jsdom\n\nYou can use canvas2d and webgl as running in web browsers.\n\n```js\nconst fs = require('fs');\nconst {Renderer, Figure2D, Mesh2D} = require('@mesh.js/core');\n\nrequire('jsdom-global')();\n\nconst {mockDOM} = require('../lib');\nmockDOM(window);\n\nconst canvas = document.createElement('canvas');\ncanvas.width = 512;\ncanvas.height = 512;\n\nconst renderer = new Renderer(canvas, {contextType: 'webgl'});\n\nconst f = new Figure2D();\nf.rect(0, 0, 512, 512);\n\nconst m = new Mesh2D(f, canvas);\nm.setFill({color: '#ddd'});\n\nconst url = 'https://p0.ssl.qhimg.com/t01a72262146b87165f.png';\n\nrenderer.loadTexture(url).then((texture) =\u003e {\n  m.setTexture(texture);\n  renderer.drawMeshes([m]);\n  fs.writeFileSync('./snapshot/snap-meshjs2.png', canvas.toBuffer());\n});\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fakira-cn%2Fnode-canvas-webgl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fakira-cn%2Fnode-canvas-webgl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fakira-cn%2Fnode-canvas-webgl/lists"}