Repository: ektogamat/threejs-andy-bolierplate
Branch: main
Commit: 6ce1c237e63b
Files: 15
Total size: 17.0 KB
Directory structure:
gitextract_t8_s_4ws/
├── .gitignore
├── 3d files/
│ └── starterscene.blend
├── LICENSE
├── bundler/
│ ├── webpack.common.js
│ ├── webpack.dev.js
│ └── webpack.prod.js
├── package.json
├── readme.md
├── src/
│ ├── debug.js
│ ├── index.html
│ ├── main.css
│ └── script.js
└── static/
├── .gitkeep
├── favicon/
│ └── site.webmanifest
└── models/
└── gltf/
└── starter-scene.glb
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
node_modules
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2022 Anderson Mancini
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: bundler/webpack.common.js
================================================
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCSSExtractPlugin = require('mini-css-extract-plugin')
const path = require('path')
module.exports = {
entry: path.resolve(__dirname, '../src/script.js'),
output:
{
hashFunction: 'xxhash64',
filename: 'bundle.[contenthash].js',
path: path.resolve(__dirname, '../dist')
},
devtool: 'source-map',
plugins:
[
new CopyWebpackPlugin({
patterns: [
{ from: path.resolve(__dirname, '../static') }
]
}),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../src/index.html'),
minify: true
}),
new MiniCSSExtractPlugin()
],
module:
{
rules:
[
// HTML
{
test: /\.(html)$/,
use:
[
'html-loader'
]
},
// JS
{
test: /\.js$/,
exclude: /node_modules/,
use:
[
'babel-loader'
]
},
// CSS
{
test: /\.css$/,
use:
[
MiniCSSExtractPlugin.loader,
'css-loader'
]
},
// Images
{
test: /\.(jpg|png|gif|svg)$/,
type: 'asset/resource',
generator:
{
filename: 'assets/images/[hash][ext]'
}
},
// Fonts
{
test: /\.(ttf|eot|woff|woff2)$/,
type: 'asset/resource',
generator:
{
filename: 'assets/fonts/[hash][ext]'
}
},
// Shaders
{
test: /\.(glsl|vs|fs|vert|frag)$/,
type: 'asset/source',
generator:
{
filename: 'assets/images/[hash][ext]'
}
}
]
}
}
================================================
FILE: bundler/webpack.dev.js
================================================
const path = require('path')
const { merge } = require('webpack-merge')
const commonConfiguration = require('./webpack.common.js')
const ip = require('internal-ip')
const portFinderSync = require('portfinder-sync')
const infoColor = (_message) =>
{
return `\u001b[1m\u001b[34m${_message}\u001b[39m\u001b[22m`
}
module.exports = merge(
commonConfiguration,
{
stats: 'errors-warnings',
mode: 'development',
devServer:
{
host: 'local-ip',
port: portFinderSync.getPort(8080),
open: true,
https: false,
allowedHosts: 'all',
hot: false,
watchFiles: ['src/**', 'static/**'],
static:
{
watch: true,
directory: path.join(__dirname, '../static')
},
client:
{
logging: 'none',
overlay: true,
progress: false
},
onAfterSetupMiddleware: function(devServer)
{
const port = devServer.options.port
const https = devServer.options.https ? 's' : ''
const localIp = ip.v4.sync()
const domain1 = `http${https}://${localIp}:${port}`
const domain2 = `http${https}://localhost:${port}`
console.log(`Project running at:\n - ${infoColor(domain1)}\n - ${infoColor(domain2)}`)
}
}
}
)
================================================
FILE: bundler/webpack.prod.js
================================================
const { merge } = require('webpack-merge')
const commonConfiguration = require('./webpack.common.js')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = merge(
commonConfiguration,
{
mode: 'production',
devtool: false,
plugins:
[
new CleanWebpackPlugin()
]
}
)
================================================
FILE: package.json
================================================
{
"repository": "#",
"license": "UNLICENSED",
"author": "Anderson Mancini <andersonmancini30@gmail.com> (https://twitter.com/Andersonmancini)",
"scripts": {
"build": "webpack --config ./bundler/webpack.prod.js",
"dev": "webpack serve --config ./bundler/webpack.dev.js",
"deploy": "vercel --prod"
},
"dependencies": {
"@babel/core": "^7.15.8",
"@babel/preset-env": "^7.15.8",
"babel-loader": "^8.2.3",
"clean-webpack-plugin": "^4.0.0",
"copy-webpack-plugin": "^9.0.1",
"css-loader": "^6.5.0",
"file-loader": "^6.2.0",
"html-loader": "^3.0.0",
"html-webpack-plugin": "^5.5.0",
"lil-gui": "^0.11.0",
"mini-css-extract-plugin": "^2.4.3",
"portfinder-sync": "0.0.2",
"raw-loader": "^4.0.2",
"style-loader": "^3.3.1",
"three": "^0.134.0",
"webpack": "^5.60.0",
"webpack-cli": "^4.9.1",
"webpack-dev-server": "^4.4.0",
"webpack-merge": "^5.8.0"
}
}
================================================
FILE: readme.md
================================================
# Ektogamat ThreeJS Boilerplate
<h4>by Anderson Mancini</h4>
<p align="center">
<img src="./static/cover.jpg" width="100%"/>
</p>
# Introduction
This is a three.js starter project with only 120 lines.
But why create another threejs boilerplate when there are already so many out there?
When I started, I had a hard time finding something simple and written in VanillaJS that would allow, with just a few lines of code, to import a model and have something functional on the screen. Also, most of the examples that exist in the threejs documentation need to be in this framework for them to work just by copying and pasting the code.
So I decided to share this starter project that tries to make things a little easier. This uses a very basic setup of Node.js to make it easier to install project dependencies and also webpack to make development easier.
I think this would be very useful for web developers, who are trying to get started with threejs. In the source file, which is all documented, you can understand the basic structure of a project in threejs.
Resources: [Threejs](https://threejs.org/), [WebGL](https://github.com/KhronosGroup/WebGL), [webpack](https://webpack.js.org/) , [Babel](https://babeljs.io/ ), [ESLint](https://eslint.org/)
# Show, don't tell
Here you can see a video on how to use this
<a href="https://www.youtube.com/embed/qM6Ih_cC6Gc" target="_blank"><img src="./static/youtube.png" width="100%"/></a>
# Getting Started
Download and install Node.js on your computer (https://nodejs.org/en/download/).
Then, open VSCODE, drag the project folder to it. Open VSCODE terminal and install dependencies (you need to do this only in the first time)
```
npm install
```
Run this command in your terminal to open a local server at localhost:8080
```
npm run dev
```
# Debug interface
<img src="./static/debug.jpg" width="100%"/>
You can enable a debug interface by getting the contents of "debug.js" file and place it in the end of the main file. This will give you some interface to change things like colors and light position, which can be very useful when you change the model to something else.
# Some projects developed using this
- https://neotix.com.br/vitrine-virtual/
- https://rava-cycle-neotix.glitch.me/
- https://vitamine-se.glitch.me/
- https://neotix.com.br/promo_havaianas/
- https://realestate-neotix.vercel.app/
- https://windland-neotix.vercel.app/
- https://dna-chain.vercel.app/
- http://houses-neotix.vercel.app/
- https://fgr-implantacao.vercel.app/
- https://metaverse-event.vercel.app/
# Notes
Would be really appreciated if you are willing to give me a star here on GitHub 🎉 or buy me a coffee ☕ https://www.buymeacoffee.com/andersonmancini. The money will be used to produce more content about threejs or to buy new courses.
I plan to add more functionality to this over time, like the ability to use advanced controls, particle systems, etc. If you have any questions or somewhere I can do better, welcome to send an <a href="mailto:andersonmancini30@gmail.com">e-mail</a> to me 🙏
================================================
FILE: src/debug.js
================================================
import { GUI } from 'three/examples/jsm/libs/dat.gui.module.js'
const gui = new GUI()
// create parameters for GUI
var params = {color: sunLight.color.getHex(), color2: ambient.color.getHex(), color3: scene.background.getHex()}
// create a function to be called by GUI
const update = function () {
var colorObj = new THREE.Color( params.color )
var colorObj2 = new THREE.Color( params.color2 )
var colorObj3 = new THREE.Color( params.color3 )
sunLight.color.set(colorObj)
ambient.color.set(colorObj2)
scene.background.set(colorObj3)
}
//////////////////////////////////////////////////
//// GUI CONFIG
gui.add(sunLight, 'intensity').min(0).max(10).step(0.0001).name('Dir intensity')
gui.add(sunLight.position, 'x').min(-100).max(100).step(0.00001).name('Dir X pos')
gui.add(sunLight.position, 'y').min(0).max(100).step(0.00001).name('Dir Y pos')
gui.add(sunLight.position, 'z').min(-100).max(100).step(0.00001).name('Dir Z pos')
gui.addColor(params,'color').name('Dir color').onChange(update)
gui.addColor(params,'color2').name('Amb color').onChange(update)
gui.add(ambient, 'intensity').min(0).max(10).step(0.001).name('Amb intensity')
gui.addColor(params,'color3').name('BG color').onChange(update)
//////////////////////////////////////////////////
//// ON MOUSE MOVE TO GET CAMERA POSITION
document.addEventListener('mousemove', (event) => {
event.preventDefault()
console.log(camera.position)
}, false)
================================================
FILE: src/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<title>Ektogamat Threejs Starter Boilerplate</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<meta name="description" content="A threejs example using threejs boilerplate - by Anderson Mancini">
<meta property="og:url" content="https://threejs-andy-boilerplate.vercel.app/">
<meta property="og:type" content="website">
<meta property="og:title" content="EKTOGAMAT THREEJS BOILERPLATE">
<meta property="og:description" content="A threejs example using threejs boilerplate - by Anderson Mancini">
<meta property="og:image" content="../static/cover.jpg">
<meta name="twitter:card" content="summary_large_image">
<meta property="twitter:domain" content="https://threejs-andy-boilerplate.vercel.app/">
<meta property="twitter:url" content="https://threejs-andy-boilerplate.vercel.app/">
<meta name="twitter:title" content="EKTOGAMAT THREEJS BOILERPLATE">
<meta name="twitter:description" content="A threejs example using threejs boilerplate - by Anderson Mancini">
<meta name="twitter:image" content="../static/cover.jpg">
<meta name="keywords" content="ThreeJS, HTML, CSS, JavaScript">
<meta name="author" content="Anderson Mancini">
<link rel="apple-touch-icon" sizes="180x180" href="../static/favicon/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="../static/favicon/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="../static/favicon/favicon-16x16.png">
<link rel="manifest" href="../static/favicon/site.webmanifest">
<meta name="msapplication-TileColor" content="#ffffff">
<meta name="theme-color" content="#ffffff">
</head>
<body>
</body>
</html>
================================================
FILE: src/main.css
================================================
body, html {
margin: 0;
padding: 0;
overscroll-behavior: none;
overflow: hidden;
}
================================================
FILE: src/script.js
================================================
/////////////////////////////////////////////////////////////////////////
///// IMPORT
import './main.css'
import * as THREE from 'three'
import { TWEEN } from 'three/examples/jsm/libs/tween.module.min.js'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js'
/////////////////////////////////////////////////////////////////////////
//// DRACO LOADER TO LOAD DRACO COMPRESSED MODELS FROM BLENDER
const dracoLoader = new DRACOLoader()
const loader = new GLTFLoader()
dracoLoader.setDecoderPath('https://www.gstatic.com/draco/v1/decoders/')
dracoLoader.setDecoderConfig({ type: 'js' })
loader.setDRACOLoader(dracoLoader)
/////////////////////////////////////////////////////////////////////////
///// DIV CONTAINER CREATION TO HOLD THREEJS EXPERIENCE
const container = document.createElement('div')
document.body.appendChild(container)
/////////////////////////////////////////////////////////////////////////
///// SCENE CREATION
const scene = new THREE.Scene()
scene.background = new THREE.Color('#c8f0f9')
/////////////////////////////////////////////////////////////////////////
///// RENDERER CONFIG
const renderer = new THREE.WebGLRenderer({ antialias: true}) // turn on antialias
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)) //set pixel ratio
renderer.setSize(window.innerWidth, window.innerHeight) // make it full screen
renderer.outputEncoding = THREE.sRGBEncoding // set color encoding
container.appendChild(renderer.domElement) // add the renderer to html div
/////////////////////////////////////////////////////////////////////////
///// CAMERAS CONFIG
const camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 100)
camera.position.set(34,16,-20)
scene.add(camera)
/////////////////////////////////////////////////////////////////////////
///// MAKE EXPERIENCE FULL SCREEN
window.addEventListener('resize', () => {
const width = window.innerWidth
const height = window.innerHeight
camera.aspect = width / height
camera.updateProjectionMatrix()
renderer.setSize(width, height)
renderer.setPixelRatio(2)
})
/////////////////////////////////////////////////////////////////////////
///// CREATE ORBIT CONTROLS
const controls = new OrbitControls(camera, renderer.domElement)
/////////////////////////////////////////////////////////////////////////
///// SCENE LIGHTS
const ambient = new THREE.AmbientLight(0xa0a0fc, 0.82)
scene.add(ambient)
const sunLight = new THREE.DirectionalLight(0xe8c37b, 1.96)
sunLight.position.set(-69,44,14)
scene.add(sunLight)
/////////////////////////////////////////////////////////////////////////
///// LOADING GLB/GLTF MODEL FROM BLENDER
loader.load('models/gltf/starter-scene.glb', function (gltf) {
scene.add(gltf.scene)
})
/////////////////////////////////////////////////////////////////////////
//// INTRO CAMERA ANIMATION USING TWEEN
function introAnimation() {
controls.enabled = false //disable orbit controls to animate the camera
new TWEEN.Tween(camera.position.set(26,4,-35 )).to({ // from camera position
x: 16, //desired x position to go
y: 50, //desired y position to go
z: -0.1 //desired z position to go
}, 6500) // time take to animate
.delay(1000).easing(TWEEN.Easing.Quartic.InOut).start() // define delay, easing
.onComplete(function () { //on finish animation
controls.enabled = true //enable orbit controls
setOrbitControlsLimits() //enable controls limits
TWEEN.remove(this) // remove the animation from memory
})
}
introAnimation() // call intro animation on start
/////////////////////////////////////////////////////////////////////////
//// DEFINE ORBIT CONTROLS LIMITS
function setOrbitControlsLimits(){
controls.enableDamping = true
controls.dampingFactor = 0.04
controls.minDistance = 35
controls.maxDistance = 60
controls.enableRotate = true
controls.enableZoom = true
controls.maxPolarAngle = Math.PI /2.5
}
/////////////////////////////////////////////////////////////////////////
//// RENDER LOOP FUNCTION
function rendeLoop() {
TWEEN.update() // update animations
controls.update() // update orbit controls
renderer.render(scene, camera) // render the scene using the camera
requestAnimationFrame(rendeLoop) //loop the render function
}
rendeLoop() //start rendering
================================================
FILE: static/.gitkeep
================================================
================================================
FILE: static/favicon/site.webmanifest
================================================
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
gitextract_t8_s_4ws/
├── .gitignore
├── 3d files/
│ └── starterscene.blend
├── LICENSE
├── bundler/
│ ├── webpack.common.js
│ ├── webpack.dev.js
│ └── webpack.prod.js
├── package.json
├── readme.md
├── src/
│ ├── debug.js
│ ├── index.html
│ ├── main.css
│ └── script.js
└── static/
├── .gitkeep
├── favicon/
│ └── site.webmanifest
└── models/
└── gltf/
└── starter-scene.glb
SYMBOL INDEX (3 symbols across 1 files)
FILE: src/script.js
function introAnimation (line 76) | function introAnimation() {
function setOrbitControlsLimits (line 96) | function setOrbitControlsLimits(){
function rendeLoop (line 108) | function rendeLoop() {
Condensed preview — 15 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (19K chars).
[
{
"path": ".gitignore",
"chars": 13,
"preview": "node_modules\n"
},
{
"path": "LICENSE",
"chars": 1073,
"preview": "MIT License\n\nCopyright (c) 2022 Anderson Mancini\n\nPermission is hereby granted, free of charge, to any person obtaining "
},
{
"path": "bundler/webpack.common.js",
"chars": 2259,
"preview": "const CopyWebpackPlugin = require('copy-webpack-plugin')\nconst HtmlWebpackPlugin = require('html-webpack-plugin')\nconst "
},
{
"path": "bundler/webpack.dev.js",
"chars": 1503,
"preview": "const path = require('path')\nconst { merge } = require('webpack-merge')\nconst commonConfiguration = require('./webpack.c"
},
{
"path": "bundler/webpack.prod.js",
"chars": 355,
"preview": "const { merge } = require('webpack-merge')\nconst commonConfiguration = require('./webpack.common.js')\nconst { CleanWebpa"
},
{
"path": "package.json",
"chars": 944,
"preview": "{\n \"repository\": \"#\",\n \"license\": \"UNLICENSED\",\n \"author\": \"Anderson Mancini <andersonmancini30@gmail.com> (https://t"
},
{
"path": "readme.md",
"chars": 3052,
"preview": "# Ektogamat ThreeJS Boilerplate\n<h4>by Anderson Mancini</h4>\n\n<p align=\"center\">\n <img src=\"./static/cover.jpg\" width"
},
{
"path": "src/debug.js",
"chars": 1427,
"preview": "import { GUI } from 'three/examples/jsm/libs/dat.gui.module.js'\nconst gui = new GUI()\n\n// create parameters for GUI\nvar "
},
{
"path": "src/index.html",
"chars": 1941,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <title>Ektogamat Threejs Starter Boilerplate</title>\n <meta c"
},
{
"path": "src/main.css",
"chars": 86,
"preview": "body, html {\n\tmargin: 0;\n\tpadding: 0;\n\toverscroll-behavior: none;\n\toverflow: hidden;\n}"
},
{
"path": "src/script.js",
"chars": 4530,
"preview": "/////////////////////////////////////////////////////////////////////////\n///// IMPORT\nimport './main.css'\nimport * as T"
},
{
"path": "static/.gitkeep",
"chars": 0,
"preview": ""
},
{
"path": "static/favicon/site.webmanifest",
"chars": 263,
"preview": "{\"name\":\"\",\"short_name\":\"\",\"icons\":[{\"src\":\"/android-chrome-192x192.png\",\"sizes\":\"192x192\",\"type\":\"image/png\"},{\"src\":\"/"
}
]
// ... and 2 more files (download for full content)
About this extraction
This page contains the full source code of the ektogamat/threejs-andy-bolierplate GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 15 files (17.0 KB), approximately 4.4k tokens, and a symbol index with 3 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.