What’s on our mind?

Collection of articles, design, site, and resources made by designers and publisher @Menu View


const canvas = document.querySelector('#webgl')

// Debug
// const gui = new dat.GUI()

const parameters = {
    color: 0x66af67,
    lightColor: 0xd7f3fa,
    rotationX: Math.PI / 8,
    rotationY: Math.PI / 4,
    spin: () => {
        gsap.to(body.rotation, {
            y: body.rotation.y + Math.PI * 2,
            duration: 1,
        });
    }
}

// Scene
const scene = new THREE.Scene()


/* Geometry */

// Material
const material = new THREE.MeshLambertMaterial({
    color: parameters.color
})

// Body
const body = new THREE.Group()
scene.add(body)
body.rotation.y = parameters.rotationY
body.rotation.x = parameters.rotationX

// Head
const head = new THREE.Group()
const createHead = () => {
    const geometry = new THREE.SphereGeometry(0.52, 30, 30)
    const main = new THREE.Mesh(geometry, material)
    head.add(main)
}

createHead()
body.add(head)
head.position.y = 0.27
head.position.x = -1.5

gsap.to(head.rotation, {
    z: 0.35,
    delay: 0.24,
    duration: 0.8,
    repeat: -1,
    yoyo: true,
    ease: 'power2.inOut'
})

// Lil spheres (body segements)
let lastPos = -1.5

for (i = 0; i <= 8; i++) {
    const s = i > 2 ? (8 - i) * 0.1 : 0.5
    const geometry = new THREE.SphereGeometry(s, 15, 15)
    const material = new THREE.MeshLambertMaterial({
        color: `rgb(${i * 30}, 175, 103)`
    })
    const mesh = new THREE.Mesh(geometry, material)

    mesh.position.x = i <= 2 ? i * 0.6 - 1.5 : lastPos + (s * 1.5)
    lastPos = mesh.position.x

    if (i > 0) {
        body.add(mesh)
    }

    gsap.to(mesh.position, {
        y: 0.16,
        delay: i * 0.24,
        duration: 0.8,
        repeat: -1,
        yoyo: true,
        ease: 'sine.inOut'
    })
}


for (i = 0; i <= 2; i++) {
    // Antenna
    const cylinderGeometry = new THREE.CylinderGeometry(0.025, 0.05, 0.7, 8)
    const antennaMaterial = new THREE.MeshLambertMaterial({
        color: 0x176654
    })
    const cylinder = new THREE.Mesh(cylinderGeometry, antennaMaterial)

    const topGeometry = new THREE.SphereGeometry(0.07, 8, 8)
    const topMesh = new THREE.Mesh(topGeometry, antennaMaterial)

    cylinder.position.x = -0.14
    cylinder.position.y = 0.36
    cylinder.position.z = i === 0 ? -0.2 : 0.2

    topMesh.position.x = -0.14
    topMesh.position.y = 0.7
    topMesh.position.z = i === 0 ? -0.2 : 0.2

    head.add(cylinder)
    head.add(topMesh)

    // Eyes
    const geometry = new THREE.SphereGeometry(0.12, 10, 10)
    const eyeMaterial = new THREE.MeshPhongMaterial({
        color: 0x2b3542
    })
    const eyeMesh = new THREE.Mesh(geometry, eyeMaterial)

    eyeMesh.position.x = -0.25
    eyeMesh.position.y = 0.18
    eyeMesh.position.z = i === 0 ? -0.36 : 0.36

    head.add(eyeMesh)
}

// Mouth
const mouthGeometry = new THREE.TorusGeometry(0.08, 0.07, 6, 15)
const mouthMaterial = new THREE.MeshPhongMaterial({
    color: 0x2b3542
})
const mouth = new THREE.Mesh(mouthGeometry, material)
head.add(mouth)

mouth.position.x = -0.48
mouth.rotation.y = Math.PI / 2

const sizes = {
    width: window.innerWidth,
    height: window.innerHeight,
}

window.addEventListener("resize", () => {
    // Update sizes
    sizes.width = window.innerWidth;
    sizes.height = window.innerHeight;
    camera.aspect = sizes.width / sizes.height;
    camera.updateProjectionMatrix();
    renderer.setSize(sizes.width, sizes.height);
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
})


// Debug
// gui.add(body.position, 'x').min(-3).max(3).step(0.01)
// gui.add(body.position, 'y').min(-3).max(3).step(0.01)
// gui.add(body, 'visible')
// gui.add(material, 'wireframe')
// gui.addColor(parameters, 'color').onChange(() => {
//   material.color.set(parameters.color)
// })
// gui.add(parameters, 'spin')

// Camera
const camera = new THREE.PerspectiveCamera(
    65,
    sizes.width / sizes.height,
    0.1,
    75
);
camera.position.z = 5
scene.add(camera)

// Lighting
const lightAmbient = new THREE.AmbientLight(0xffea63)
const lightDirectional = new THREE.DirectionalLight(0xffffff, 0.5)
scene.add(lightAmbient)
scene.add(lightDirectional)

lightDirectional.position.x = 1.5
lightDirectional.position.y = 1.5
lightDirectional.position.z = 3

// Controls
const controls = new THREE.OrbitControls(camera, canvas);
controls.enableDamping = true;


// Renderer
const renderer = new THREE.WebGLRenderer({
    canvas,
    alpha: true
});
renderer.setSize(sizes.width, sizes.height);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.setClearColor(0xc4ddff, 1)
renderer.render(scene, camera)

// Animate
const clock = new THREE.Clock()

const tick = () => {
    const elapsedTime = clock.getElapsedTime()

    // Update controls
    controls.update()

    // Render
    renderer.render(scene, camera)

    // Call tick again on the next frame
    window.requestAnimationFrame(tick)
}

tick()