{"id":25808885,"url":"https://github.com/anthonygress/threejs-start","last_synced_at":"2026-04-20T10:32:53.826Z","repository":{"id":160631091,"uuid":"393455362","full_name":"AnthonyGress/threejs-start","owner":"AnthonyGress","description":"A boilerplate for three js projects, this one has a stargate model inside of a starship.","archived":false,"fork":false,"pushed_at":"2021-08-10T22:10:53.000Z","size":20486,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-08-04T21:49:34.980Z","etag":null,"topics":["html","javascript","threejs","webgl","webpack"],"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/AnthonyGress.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":"2021-08-06T17:42:15.000Z","updated_at":"2025-07-23T07:37:59.000Z","dependencies_parsed_at":"2023-05-10T20:00:31.958Z","dependency_job_id":null,"html_url":"https://github.com/AnthonyGress/threejs-start","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/AnthonyGress/threejs-start","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AnthonyGress%2Fthreejs-start","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AnthonyGress%2Fthreejs-start/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AnthonyGress%2Fthreejs-start/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AnthonyGress%2Fthreejs-start/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AnthonyGress","download_url":"https://codeload.github.com/AnthonyGress/threejs-start/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AnthonyGress%2Fthreejs-start/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32043038,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-20T00:18:06.643Z","status":"online","status_checked_at":"2026-04-20T02:00:06.527Z","response_time":94,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["html","javascript","threejs","webgl","webpack"],"created_at":"2025-02-27T22:38:48.448Z","updated_at":"2026-04-20T10:32:53.806Z","avatar_url":"https://github.com/AnthonyGress.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003eThreeJS Starter\u003c/h1\u003e\n\n\u003ch3 align=\"center\"\u003e\nA three.js starter based on object-oriented programming.\n\u003ch3/\u003e\n\n\u003cp align=\"center\"\u003e\n\u003cimg width=\"1440\" alt=\"Screen Shot 2021-08-07 at 5 36 13 PM\" src=\"https://user-images.githubusercontent.com/70029654/128614445-4a96962f-e59a-435d-b44f-f51c7c5b899e.png\"\u003e\n\n\u003c/p\u003e\n\n## Table of Contents\n- [Introduction](#introduction)\n- [Getting Started](#getting-started)\n- [Event Emitter](#event-emitter)\n- [Mouse Control](#mouse-control)\n- [Add Material](#add-material)\n- [Create Mesh](#create-mesh)\n- [Load Model](#load-model)\n- [Customized Shader](#customized-shader)\n- [GSAP Animation](#gsap-animation)\n- [Debug Mode](#debug-mode)\n- [Notes](#notes)\n\n## Introduction\n\nThis is a three.js starter written in OOP based on [Bruno Simon's project](https://github.com/brunosimon/folio-2019) of this [cool website](https://bruno-simon.com/). I think this would really help for web developers, who are trying to use three.js library in a more modular and manageable way in their future projects. It's also a quick guide to understand some basic structure of Bruno Simon's project.\n\nTo use this starter, I would go through some main steps. However, you don't need to complete the entire document before getting started, instead, you can come back if needed while developing your own project.\n\nFeatures: [Three.js](https://threejs.org/), [WebGL](https://github.com/KhronosGroup/WebGL), [GSAP](https://greensock.com/gsap/), [dat.gui](https://github.com/dataarts/dat.gui), [webpack](https://webpack.js.org/), [Babel](https://babeljs.io/), [ESLint](https://eslint.org/), OOP\n\n## Getting Started\nType the following steps into the terminal.\n\n```bash\ngit clone https://github.com/TomHsiao1260/three-starter.git\n\ncd three-starter\n\nnpm install\n```\n\nThen start the dev server and navigate to http://localhost:8080\n\n```bash\nnpm run dev\n```\n\n## Event Emitter\n\n`Event Emitter` allows us to handle some custom events. For example, we can create an instance of `Time` class which is inherited from `Event Emitter`.\n\n```javascript\n// src/javascript/Application.js\n\nthis.time = new Time();\n```\n\nLet's put some callbacks via `Event Emitter` built-in `on` method.\n\n```javascript\nthis.time.on('tick', myCallback);\n```\n\nNow, we can use `trigger` method to execute those callbacks. Note that it would only trigger events with the same name (`tick` in this example).\n\n```javascript\nthis.time.trigger('tick');\n```\n\nYou can also remove callbacks via `remove` method.\n\n```javascript\nconst event = this.time.on('tick', myCallback);\n\nthis.time.remove(event);\n```\n\nIn `src/javascript/Utils/Time.js`, the `trigger('tick')` would be executed for each frame, so callbacks such as `this.time.on('tick', ... )` would be automatically triggered once for each frame. In addition, the `Resources` class in this starter is also inherited from `Event Emitter` and callbacks such as `this.resources.on('ready', ... )` would be triggered after all resources are loaded.\n\nYou can also append multiple names to the callback, such as `.on('name1/name2/...', ... )`, so that both `.trigger('name1')` and `.trigger('name2')` will trigger the same callback. If you want to send parameters to the callback, you can use `.trigger('name', [para1, para2, ...])` which would send `para1` and `para2` as the first and second parameters to the callback, respectively.\n\n## Mouse Control\n\nThe mouse controls are written in `src/javascript/World/Controls.js`. The instance of this class can be created as follows.\n\n```javascript\n// src/javascript/World/index.js\n\nthis.controls = new Controls({\n    time: this.time,\n    sizes: this.sizes,\n});\n```\nYou can put some callbacks after the mouse is pressed or released via the `Event Emitter`, or receive the current mouse position in Normalized Device Coordinate (NDC) as follows.\n\n```javascript\n// execute when pressing down the mouse button\nthis.time.on('mouseDown', myCallback);\n\n// execute when releasing the mouse button\nthis.time.on('mouseUp', myCallback);\n\n// current mouse x position: [-1, 1] from left to right\nthis.controls.mouse.x;\n\n// current mouse y position: [-1, 1] from bottom to top\nthis.controls.mouse.y;\n```\n\nYou can write your own custom events in `src/javascrpt/World/Controls.js` such as double click, keyboard controls, Raycaster, etc.\n\n## Add Material\n\nTo create a material with external resources (ex: matcap), you can do the following steps. First, import the resources and put them into an array of objects.\n\n```javascript\n// src/javascript/Resources.js\n\nimport matcapGoldSource from '../models/matcaps/gold.png';\n\nthis.loader.load([\n    { name: 'matcapGold', source: matcapGoldSource },\n    ...\n]);\n\n```\n\nThen create the material.\n\n```javascript\n// src/javascript/World/Materials.js\n\nconst { matcapGold } = this.resources.items;\n\nthis.items.matcap.gold = new THREE.MeshMatcapMaterial({ matcap: matcapGold });\n```\n\nNow, we can create the mesh using this material.\n\n```javascript\n// src/javascript/World/Torus.js\n\nconst material = this.material.items.matcap.gold;\nconst mesh = new THREE.Mesh(geometry, material);\n```\n\n## Create Mesh\n\nLet's create a `THREE.Mesh` (ex: Torus) and add it to the scene. First, create a `Torus` class which can generate the `THREE.mesh`.\n\n```javascript\n// src/javascript/World/Torus.js\n\nconst geometry = new THREE.TorusGeometry(0.25, 0.08, 32, 100);\nconst material = this.material.items.matcap.gold;\nconst mesh = new THREE.Mesh(geometry, material);\n\nthis.container.add(mesh);\n```\n\nThen create an instance of this class. Note that the mesh would be added to the scene graph via the connection of `container` property, which is a `THREE.Object3D`.\n\n```javascript\n// src/javascript/World/index.js\n\nimport Torus from './Torus';\n\nstart() {\n    ...\n    this.setTorus();\n}\n\nsetTorus() {\n    this.torus = new Torus({\n        material: this.material,\n    });\n    this.container.add(this.torus.container);\n}\n```\n\n## Load Model\n\nThe following steps can load some external models (ex: `.glb`, `.fbx`, etc.). First, we need to import our model file.\n\n```javascript\n// src/javascript/Resources.js\n\nimport foxSource from '../models/fox/glTF-Binary/Fox.glb';\n\nthis.loader.load([\n    { name: 'fox', source: foxSource },\n    ...\n]);\n```\n\nThen create a class and add the loaded model to the `container` property.\n\n```javascript\n// src/javascript/World/Fox.js\n\nconst gltf = this.resources.items.fox;\nthis.container.add(gltf.scene);\n```\n\nNow we can create an instance of this class (just like [Create Mesh](#create-mesh)).\n\n```javascript\n// src/javascript/World/index.js\n\nimport Fox from './Fox';\n\nstart() {\n    ...\n    this.setFox();\n}\n\nsetFox() {\n    this.fox = new Fox({\n        resources: this.resources,\n    });\n    this.container.add(this.fox.container);\n}\n```\n\nFor simplicity, this starter only support `.glb`, `.gltf` and `.fbx` extensions. Draco compression would automatically support for models with `.glb` extension. If you want to load models with other extensions, you can check out `src/javascript/Utils/Loader.js` and write some custom model loaders.\n\n## Customized Shader\n\nYou can create some customized shader materials as follows.\n\n```javascript\n// src/javascript/Materials/Plane.js\n\nimport vertexShader from '../../shaders/plane/vertex.glsl';\nimport fragmentShader from '../../shaders/plane/fragment.glsl';\n\nexport default function PlaneMaterial() {\n    const uniforms = {\n        uTime: { value: 0 },\n    };\n\n    const material = new THREE.ShaderMaterial({\n        vertexShader,\n        fragmentShader,\n        uniforms,\n    });\n\n    return material;\n}\n```\n\nThen create an instance object of this material.\n\n```javascript\n// src/javascript/World/Materials.js\n\nimport PlaneMaterial from '../Materials/Plane';\n\n...\n\nthis.items.shader.plane = new PlaneMaterial();\n```\n\nNow, we can use this shader material to create a plane mesh (just like [Create Mesh](#create-mesh)).\n\n```javascript\n// src/javascript/World/Plane.js\n\nconst geometry = new THREE.PlaneGeometry(1.2, 0.7, 50, 50);\nconst material = this.material.items.shader.plane;\nconst mesh = new THREE.Mesh(geometry, material);\n```\n\n## GSAP Animation\n\nTo handle animations in Three.js, one of the well-known options is [GSAP](https://greensock.com/gsap/) which is a blazing fast library for JavaScript animations. For convenience and predictable behavior, we use only single [GSAP Timeline](https://greensock.com/docs/v3/GSAP/Timeline) and apply [GSAP Labels](https://greensock.com/docs/v3/GSAP/Timeline/addLabel()) to handle all animations. \n\n```javascript\n// src/javascript/World/Transition.js\n\n// all animations should be applied via this timeline\nthis.timeline = gsap.timeline();\n\n...\n\nconst targetA = this.camera.instance.position;\nconst targetB = this.light.directionalLight;\nconst targetC = this.light.ambientLight;\n\nthis.timeline.to(targetA, { z: 1, duration: 2 }, 'firstTransition');\nthis.timeline.to(targetB, { intensity: 0.8, duration: 3 }, '\u003c');\nthis.timeline.to(targetC, { intensity: 0.8, duration: 3 }, '\u003c');\n\nreturn this.timeline;\n```\n\nIn `GSAP 3`, the `timeline` can be a promise which would be resolved when all animations are complete. As a result, we can return `this.timeline` at the end of the function, and then use `await` to manage the animation workflow as follows.\n\n```javascript\n// src/javscript/World/index.js\n\nasync start() {\n    ...\n    await this.transition.firstTransition();\n    await this.transition.secondTransition();\n    ...\n}\n```\n\nThis can ensure that `.secondTransition()` would not be executed until `.firstTransition()` is resolved. You can also add some procedures between each transition function if needed.\n\n## Debug Mode\n\nTo enter the debug mode, you can append `#debug` at the end of the route http://localhost:8080/#debug. It would have [stats.js](https://github.com/mrdoob/stats.js/) performance monitor, and [dat.gui](https://github.com/dataarts/dat.gui) controller on the upper left and upper right, respectively.\n\nIn order to tune some parameters via `dat.gui` (ex: visibility of a Torus), you can do the following steps.\n\n```javascript\n// src/javascript/World/Torus.js\n\n// create dat.gui folder\nif (this.debug) {\n    this.debugFolder = this.debug.addFolder('torus');\n    this.debugFolder.open();\n}\n\n// add some tuning parameters to the 'torus' folder\nif (this.debug) {\n    this.debugFolder.add(mesh, 'visible').name('visible');\n    ...\n}\n```\n\nSometimes, printing out the whole scene graph is helpful. In this starter, the entire application has already been added to the global `window` object, so that you can easily print it out by typing `window.application` on your browser [console panel](https://developer.chrome.com/docs/devtools/console/).\n\n```javascript\n// src/index.js\n\nwindow.application = new Application({\n    $canvas: document.querySelector('.js-canvas'),\n});\n```\n\nNote that the current `dat.gui` library is installed locally instead of using `npm i dat.gui` in order to remove the ReDos [vulnerabilities warning](https://www.npmjs.com/advisories/1701) which is harmless when developing a frontend-only projects. Actually, this issue has already been [fixed and merged](https://github.com/dataarts/dat.gui/pull/279) recently and the warning will disappear after both `dat.gui` new version and `npm` security advisories have been updated. I will keep an eye on that and update this starter until `dat.gui` vulnerabilities warning disappears 🙂\n\n## Notes\n\nWould be really appreciated if you are willing to give me a star 🎉\n\nIf you have any questions or somewhere I can do better, welcome to send an issue or e-mail to me 🙏\n\ne-mail: tomtogo1260@gmail.com\n    \n\n    \nForked from [TomHsiao1260](https://github.com/TomHsiao1260)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanthonygress%2Fthreejs-start","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fanthonygress%2Fthreejs-start","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanthonygress%2Fthreejs-start/lists"}