{"id":13602410,"url":"https://github.com/poki/three-ui","last_synced_at":"2025-10-02T21:30:43.899Z","repository":{"id":51695287,"uuid":"105753991","full_name":"poki/three-ui","owner":"poki","description":"UI solution for Three.js","archived":true,"fork":false,"pushed_at":"2021-05-10T17:57:51.000Z","size":153,"stargazers_count":178,"open_issues_count":13,"forks_count":29,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-09-29T05:21:52.532Z","etag":null,"topics":["interface","three-js","threejs","ui"],"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/poki.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}},"created_at":"2017-10-04T09:59:46.000Z","updated_at":"2024-09-06T08:49:31.000Z","dependencies_parsed_at":"2022-09-03T04:21:02.501Z","dependency_job_id":null,"html_url":"https://github.com/poki/three-ui","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/poki%2Fthree-ui","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/poki%2Fthree-ui/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/poki%2Fthree-ui/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/poki%2Fthree-ui/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/poki","download_url":"https://codeload.github.com/poki/three-ui/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":235043602,"owners_count":18927006,"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":["interface","three-js","threejs","ui"],"created_at":"2024-08-01T18:01:22.426Z","updated_at":"2025-10-02T21:30:38.588Z","avatar_url":"https://github.com/poki.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# ThreeUI\n\nUI solution for [Three.js](http://threejs.org/).\n\nBasic layout system that will draw UI elements (rectangles, text, sprites) on a canvas, and will render this canvas on a quad in a separate Three.js scene.\n\n## Usage\n\nOnce you make sure you have three.js and three-ui loaded you can get started quite easily.\n\n```js\n// Setup THREE.WebGLRenderer\nconst renderer = new THREE.WebGLRenderer();\ndocument.body.appendChild(renderer.domElement);\n\nconst ui = new ThreeUI(renderer.domElement, 720);\n\n// Create things\nconst rectangle = ui.createRectangle('#FF6D92', 0, 0, 250, 250);\n\n// Render!\nui.render(renderer);\n```\n\n## Basic Example\n\nFull source can be found in `examples/` ([here](examples/basic.html)).\n\n```js\n// Create a new THREE.WebGLRenderer\nconst renderer = new THREE.WebGLRenderer({ alpha: true });\nrenderer.setSize(window.innerWidth, window.innerHeight);\n\ndocument.body.appendChild(renderer.domElement);\n\n// Create a UI of 720 pixels high\n// will scale up to match renderer.domElement's size\nconst ui = new ThreeUI(renderer.domElement, 720);\n\n// Place a Pretty Pink 500x150 rectangle in the center of the screen\nconst rectangle = ui.createRectangle('#FF6D92', 0, 0, 500, 100);\nrectangle.anchor.x = ThreeUI.anchors.center;\nrectangle.anchor.y = ThreeUI.anchors.center;\n\n// Add some text to the rectangle\nconst text = ui.createText('BEST BUTTON EVER', 40, 'Arial', 'white');\ntext.textAlign = 'center';\ntext.textBaseline = 'middle';\ntext.anchor.x = ThreeUI.anchors.center;\ntext.anchor.y = ThreeUI.anchors.center;\n\ntext.parent = rectangle;\n\n// Give the rectangle a click handler\nrectangle.onClick(() =\u003e {\n\tconsole.info('You got me!');\n});\n\n// Animate that rectangle!\nconst animate = (deltaTime = 0) =\u003e {\n\trectangle.x = Math.sin(deltaTime / 500) * 100;\n\trectangle.y = Math.cos(deltaTime / 500) * 100;\n\n\tui.render(renderer);\n\n\trequestAnimationFrame(animate);\n};\nanimate();\n```\n\n## More examples\n\nThe following example is more like an appendix and won't fully run, will split this up into working examples later.\n\nThis project comes with an asset loader as well (for now at least). You can use your own asset loading implementation, but ThreeUI depends on `AssetLoader.getAssetById` to exist and return correct objects. \n\nFor now I would recommend using the provided asset loader to make sure everything works properly.\n\n```js\n// Add assets to the asset loader\nAssetLoader.add.webFont('webFont', 'fonts/web-font.css');\nAssetLoader.add.image('sprites/asset.png');\nAssetLoader.add.image('sprites/asset-active.png');\nAssetLoader.add.spriteSheet('sprites/sheet.png', 'sprites/sheet.json');\nAssetLoader.add.bitmapText('fonts/bitmap-font.png', 'fonts/bitmap-font.json');\n\n// Set a progress listener, can be used to create progress bars\nAssetLoader.progressListener = function(progress) {\n\tconsole.info('Progress: ' + (progress * 100) + '%');\n};\n\n// Load, and start game when done\nAssetLoader.load(function() { // This function is called when all assets are loaded\n\t// Initialize the game\n\tinit();\n});\n\n// Inside of Game\n\nfunction init () {\n \t// Init the UI with the game canvas, renderer.domElement from Three.js\n\t// Second argument determines UI height in pixels\n\t// the ui will always stretch to the full game canvas but these pixels are used for calculations\n\tthis.ui = new ThreeUI(this.canvas, 720); // this.canvas is the canvas your game is rendered in\n\n\t// We like pixels\n\tthis.ui.texture.minFilter = THREE.NearestFilter;\n\tthis.ui.texture.magFilter = THREE.NearestFilter;\n\n\t// Create a new rectangle\n\tvar rectangle = this.ui.createRectangle('#ffffff', 0, 0, 1280, 50);\n\trectangle.alpha = .8;\n\trectangle.anchor.x = ThreeUI.anchors.center;\n\trectangle.anchor.y = ThreeUI.anchors.center;\n\n\t// Create a new sprite\n\tsprite = ui.createSprite('sprites/asset.png');\n\tsprite.alpha = 1; // Default\n\tsprite.x = 50;\n\tsprite.y = 50;\n\tsprite.pivot.x = 0.5; // Default\n\tsprite.pivot.y = 0.5; // Default\n\tsprite.anchor.x = ThreeUI.anchors.left; // Default\n\tsprite.anchor.y = ThreeUI.anchors.top; // Default\n\tsprite.parent = rectangle; // You can base the sprite's position on another DisplayObject's bounds by setting it as its parent\n\n\t// You can also stretch a display object, and adjust it's final position / dimensions with offset (this works with parent)\n\t// Please note that setting stretch to true will mean the coordinates and dimensions you've set for that dimension will be ignored\n\t// i.e. stretch.x = true will mean x, width and anchor.x values are ignored\n\tvar stretchRectangle = this.ui.createRectangle('#ffffff', 0, 0, 1280, 50);\n\tstretchRectangle.alpha = .8;\n\tstretchRectangle.stretch.x = true;\n\tstretchRectangle.offset.left = 50;\n\tstretchRectangle.offset.right = '50%'; // Offsets can also be in %\t\n\n\t// Create text (text, font, color)\n\tvar text = this.ui.createText('Hello World!', 20, 'webFont', '#ffffff');\n\ttext.y = 50;\n\ttext.anchor.x = ThreeUI.anchors.center;\n\ttext.anchor.y = ThreeUI.anchors.top;\n\ttext.textAlign = 'center';\n\n\t// Create BitmapText (text, scale, x, y, sheetImagePath, sheetDataPath)\n\tvar bitmapText = this.ui.createBitmapText('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@', 1, 0, 0, 'fonts/bitmap-font.png', 'fonts/bitmap-font.json');\n\tbitmapText.anchor.x = ThreeUI.anchors.left;\n\tbitmapText.anchor.y = ThreeUI.anchors.top;\n\tbitmapText.pivot.x = 0.5; // Bitmap alignment is also done through pivot, 0,0 is default for BitmapText\n\tbitmapText.smoothing = false; // For pixel fonts make sure to set smoothing to false (this also works for sprites!)\n\n\t// Update bitmaptext text by calling setText\n\tbitmapText.setText('OTHERTEXT');\n\n\t// Note: Sprites, Rectangles, Text and BitmapText are all DisplayObjects and have mostly the same methods and properties available to them.\n\n\tsprite.onClick(function(sprite) {\n\t\tconsole.log(\"You've clicked sprite!\");\n\t});\n\n\tsprite.setAssetPath('sprites/asset-active.png'); // Change the sprite's asset by using setAssetPath\n\n\t// Create a sprite from a sheet\n\tvar spriteFromSheet = ui.createSpriteFromSheet('asset-in-sheet.png', 'sprites/sheet.png', 'sprites/sheet.json');\n\n\tspriteFromSheet.setAssetPath('other-asset-in-sheet.png'); // Change the sprite to a different one within this sheet\n\tspriteFromSheet.setAssetPath('asset-from-other-sheet.png', 'sprites/other-sheet.png', 'sprites/other-sheet.json'); // Change the sprite to a different one in a different sheet\n\n\tanimate();\n}\n\nfunction animate() {\n\tupdate();\n\trender();\n\t\n\trequestAnimationFrame(animate);\n}\n\nfunction update() {\n\t// Sprites can be animated simply by adjusting their values\n\tsprite.x += 1;\n}\n\nfunction render() {\n\t// Your three js renderer\n\trenderer.render(this.scene, this.camera); // Render the game with the game's camera\n\tthis.ui.render(game.renderer); // Render the UI in it's own scene in the game's renderer\n}\n```\n\n## Spritesheets\n\nWe have basic spritesheet support. We use the free version of Texturepacker, and export to JSON (Array). We only support the \"filename\" and \"frame\" keys in this format, so the other values can be stripped.\n\nExample stripped down unminified sheet.json:\n\n```json\n{\n\t\"frames\": [\n\t\t{\n\t\t\t\"filename\": \"sprite.png\",\n\t\t\t\"frame\": {\n\t\t\t\t\"x\": 0,\n\t\t\t\t\"y\": 0,\n\t\t\t\t\"w\": 100,\n\t\t\t\t\"h\": 100\n\t\t\t}\n\t\t}\n\t]\n}\n```\n\n## Bitmap fonts\n\nWe have basic bitmap font support. We accept a json that contains UV coordinates per character.\n\nExample stripped down unminified sheet.json:\n\n```json\n{\n\t\"A\": { \n\t\t\"uv0\": [0.0078125, 0.9921875],\n\t\t\"uv1\": [0.109375, 0.890625]\n\t}\n}\n```\n\n## Wishlist\n\n- ES6\n- eslint\n- Naming of methods like 'DisplayObject:determinePositionInCanvas' and 'DisplayObject:getOffsetInCanvas' could be clearer\n- Allow % values for all position / dimensions (not just offset)\n- Unit testing\n- Completely functional rotation\n- Advanced spritesheet features such as trimmed or rotated sprites\n- Non-square event handling bounding boxes\n- Separate render logic from \"Three.js logic\", so other renderers (like PIXI.js) can be used instead\n\n## Known limitations / bugs\n\n- Rotation isn't functional with bounding boxes and therefore event listeners\n- Rotation doesn't respect pivot on a stretched DisplayObject\n\n### Misc.\n\nThanks to Evermade's Jaakko for the [blog post](https://www.evermade.fi/en/pure-three-js-hud/) that inspired this project.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpoki%2Fthree-ui","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpoki%2Fthree-ui","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpoki%2Fthree-ui/lists"}