I love 3d effects on the internet, and they're increasingly all over the place. They can add an extra dimension to a website that helps to catch a user's attention. I have previously covered 3d effects with the WebGL cards I made, but in this guide, I wanted to create a morphing sphere with a cool, wireframe background. Through this tutorial, I'll also help you understand the basics of how 3d effects work on the web. Let's get started. Here is the demo Creating a 3D Morphing Sphere The tool I use for accessing WebGL functionality in the browser is called three.js. It's a package that simplifies the process of doing 3d work in the browser - and to do this, it uses . We'll append this through code later. canvas It's important to understand that three.js simply gives us an interface to work with WebGL, which is an API for rendering 2d and 3d objects on the web. that's why we'll import three.js next. You can do this through . The two things we're going to want to install here are as follows: npm npm i three npm i open-simplex-noise npm install three-orbitcontrols Import these into your code once they're installed. Since I did my demo on codepen, I imported them using skypack. These three functions will let us do three (pun not intended) things: use 3d shapes on the web (three) control the camera (three-orbitcontrols) create noise and randomness (open-simplex-noise) import * as THREE from "https://cdn.skypack.dev/three@0.133.1"; import { OrbitControls } from "https://cdn.skypack.dev/three@0.133.1/examples/jsm/controls/OrbitControls.js"; import openSimplexNoise from 'https://cdn.skypack.dev/open-simplex-noise'; Setting up our scene for 3d objects in three.js After we've imported our packages, we want to do a few things: , for our 3d objects to sit on create a new scene , so we can look at our scene create a camera , and set its size so we don't get weird fuzzy shapes create a renderer , so we can click and drag our object and move it around add in our orbital controls // Scene let scene = new THREE.Scene(); // Camera let camera = new THREE.PerspectiveCamera( 75, innerWidth / innerHeight, 0.1, 1000 ); camera.position.set(1.5, -0.5, 6); // Renderer let renderer = new THREE.WebGLRenderer({antialias: true, alpha: true}); renderer.setSize( innerWidth, innerHeight ); // Append our renderer to the webpage. Basically, this appends the `canvas` to our webpage. document.body.appendChild( renderer.domElement ); new OrbitControls(camera, renderer.domElement); Now, the fun can begin, and we can start adding in our 3d shapes. Adding 3d shapes to our scene with three.js First up, is our sphere. Each object in three.js consists of two parts - the , which is the vertices and triangles that make up the sphere, and the , which is the colors, patterns, and features of those vertices. geometry mesh Since we want to manipulate all of our vertices ultimately, I am going to store them all separately in a array too. We will use the built in function to store sets of 3d coordinates in three.js. positionData Vector3 // Create our geometry let sphereGeometry = new THREE.SphereGeometry(1.5, 100, 100); // This section is about accessing our geometry vertices and their locations sphereGeometry.positionData = []; let v3 = new THREE.Vector3(); for (let i = 0; i < sphereGeometry.attributes.position.count; i++){ v3.fromBufferAttribute(sphereGeometry.attributes.position, i); sphereGeometry.positionData.push(v3.clone()); } // A `normal` material uses the coordinates of an object to calculate its color let sphereMesh = new THREE.MeshNormalMaterial(); // Combine both, and add it to the scene. let sphere = new THREE.Mesh(sphereGeometry, sphereMesh); scene.add(sphere); Using Custom Shaders Instead Now, caveat here, I decided to make my sphere a little more customizable, and to do that, I used . So, when we call , it actually does something a bit unusual for the web. It uses something called shaders to calculate the color of each vertex. one shaders MeshNormalMaterial There are two types of shaders, , which is essentially the colors of the object, and , which is the position of the vertices on that shape. These shaders are written in or - so not Javascript. I'm not going to go into detail about how this language works, but it's a little bit more like C than Javascript. fragment vertex GLSL OpenGL Shading Language The fundamentals are: Instead of using , we can use , and build our own shaders. MeshNormalMaterial ShaderMaterial We will use shaders - so the same effect will occur, but having them in our code means we can update it later - for example, change the colors. Normal Material We can pass Javascript variables into the shader in real time using , which are a special type of variable in GLSL. uniforms That means we define our GLSL in the HTML, and pull it in with a Javascript selector. : I haven't made any real changes to these shaders compared to - the only difference is I am passing in a color as a . That means we can change this value from Javascript if we want. I'll only show the fragment shader here, but both can be found in the . that I define - that's the variable we will be using from our Javascript! Note MeshNormalMaterial uniform HTML section on codepen Notice uniform vec3 colorA Ultimately, shaders make a manipulation for every pixel of our 3D object based on what we tell them to do. They are computationally expensive, but can create some pretty cool effects. <script id="fragment" type="text/glsl"> uniform vec3 colorA; #define NORMAL #if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP ) varying vec3 vViewPosition; #endif #include <packing> #include <uv_pars_fragment> #include <normal_pars_fragment> #include <bumpmap_pars_fragment> #include <normalmap_pars_fragment> #include <logdepthbuf_pars_fragment> #include <clipping_planes_pars_fragment> void main() { #include <clipping_planes_fragment> #include <logdepthbuf_fragment> #include <normal_fragment_begin> #include <normal_fragment_maps> gl_FragColor = vec4( normalize( normal ) * colorA + 0.5, 1.0 ); #ifdef OPAQUE gl_FragColor.a = 1.0; #endif } </script> How normal shaders work A normal shader calculates the color of a pixel by the calculation . As such, we can swap out the first for a custom color, that being our . We can then add both of these vertex and fragment shader to our Javascript like so: normalize( normal ) * 0.5 + 0.5 0.5 uniform colorA let sphereMesh = new THREE.ShaderMaterial({ uniforms: { colorA: {type: 'vec3', value: new THREE.Vector3(0.5, 0.5, 0.5)}, }, vertexShader: document.getElementById('vertex').textContent, fragmentShader: document.getElementById('fragment').textContent, }); It's important when learning WebGL to know that this is how it works under the hood. Shaders are really important to do things in 3d - so it's good to know how to define them and manipulate them. Manipulating our Sphere's Geometry We can then create a three.js clock to track time. We use that time to create noise, using our imported noise function. Noise is just randomness that will help create the effect of a randomly morphing sphere. After that, three.js also provides a function for us to add animation keyframes, and we can use the aforementioned three.js clock to animate that sphere. let noise = openSimplexNoise.makeNoise4D(Date.now()); let clock = new THREE.Clock(); renderer.setAnimationLoop( () => { // Get the time let t = clock.getElapsedTime(); sphereGeometry.positionData.forEach((p, idx) => { // Create noise for each point in our sphere let setNoise = noise(p.x, p.y, p.z, t * 1.05); // Using our Vector3 function, copy the point data, and multiply it by the noise // this looks confusing - but it's just multiplying noise by the position at each vertice v3.copy(p).addScaledVector(p, setNoise); // Update the positions sphereGeometry.attributes.position.setXYZ(idx, v3.x, v3.y, v3.z); }) // Some housekeeping so that the sphere looks "right" sphereGeometry.computeVertexNormals(); sphereGeometry.attributes.position.needsUpdate = true; // Render the sphere onto the page again. renderer.render(scene, camera); }) Now our sphere will start morphing! I repeated this for the plane behind the sphere, too. I used a here, with just a basic mesh, that makes it look like a wireframe. The code for that bit, along with everything else, . BoxGeometry is available on CodePen Conclusion Making 3d shapes on the web is a great frontend skill to have. Although a lot can be done in CSS and HTML, ome effects can only be achieved through 3d, and three.js provides the perfect platform to do that on. I hope you've enjoyed this quick guide to creating a 3d morphing sphere in three.js and Javascript. If you'd like more Javascript content, . you can read all my other stuff here Also published here.