How to Build a 3D Medal System with WebGL and Three.js
This article explains how to use WebGL and the Three.js library to create a cross‑platform 3D medal system, covering model format choices, scene setup, rendering code, performance optimizations such as gzip compression, caching, skin switching, and graceful fallback for older devices.
Introduction
WebGL is a 3D drawing protocol that combines JavaScript with OpenGL to provide GPU‑accelerated rendering inside a browser canvas. It enables data visualization, AR/VR, and 3D game animations across platforms. Because raw WebGL only draws points, lines, and triangles, developers typically adopt higher‑level frameworks like Three.js to build complex scenes.
Medal System Design
The medal system aims to boost user engagement by displaying 3D medals that evolve through levels, trigger on specific actions, and support interactive effects on mobile devices. The chosen technology stack is a WebGL‑based front‑end displayed in a WebView for both Android and iOS, with Three.js as the rendering engine.
Model Format Selection
Several 3D model formats supported by Three.js were evaluated:
JSON (*.js / *.json) : native Three.js format, easy to declare geometry and materials, but parsing in a WebView can be slow.
OBJ + MTL (*.obj / *.mtl) : simple geometry definition, lacks animation support, requires separate material files.
Collada (*.dae) : XML‑based, widely supported by 3D tools, good compatibility.
STL (*.stl) : fast generation, mainly for 3D printing, not ideal for web rendering.
FBX (*.fbx) : rich metadata for models, materials, and animations; broad tool support but may be converted to mesh on some platforms.
CTM (*.ctm) : compressed mesh format with high compression ratio, but the algorithm is complex.
VTK (*.vtk) : visualization toolkit format, limited support in Three.js.
PLY (*.ply) : polygon format used by 3D printers.
After testing, the team exported models as Collada (.dae) files using C4D, because the format offers good compatibility and an XML‑based structure.
Running Three.js
Three.js abstracts WebGL APIs, handling scene, camera, renderer, lights, and textures. The typical workflow includes importing the library, loading model loaders, initializing the scene, and animating the objects.
import 'three';
import 'three/examples/js/loaders/ColladaLoader2';
import 'three/examples/js/loaders/MTLLoader'; function init() {
initRender();
initScene();
initCamera();
initLight();
}
function loadModel() {
mesh = new THREE.Mesh();
let mtl = new THREE.MTLLoader();
let loader = new THREE.ColladaLoader();
mtl.load('../model/medal.mtl', function (result) {
result.preload();
let { materials } = result;
loader.load('../model/medal.dae', function (dae) {
for (let key in dae.library.materials) {
let name = dae.library.materials[key].name;
if (materials[name]) {
Object.assign(dae.library.materials[key].build, materials[name]);
}
if (name === 'font') {
dae.library.materials[key].build.blending = THREE.NoBlending;
dae.library.materials[key].build.needsUpdate = true;
}
}
mesh = dae.scene.children[0].clone();
scene.add(mesh);
});
});
}
function animate() {
requestAnimationFrame(animate);
mesh.rotation.y += 0.01;
render();
}Performance Optimizations
Skin Switching
Introduce an additional MTL file containing alternative material definitions. The front‑end receives a JSON payload with URLs for the material file, gzipped model, and placeholder images.
{
"image3DMaterial": "http://example.com/medal.mtl",
"image3DMoudle": "http://example.com/medal.gz",
"imageGot": "http://example.com/earned.png",
"imageNotget": "http://example.com/unearned.png"
}Gzip Compression for Large Models
Because the CDN does not support on‑the‑fly gzip, a Node.js script pre‑compresses .dae and .mtl files before uploading. At runtime, a WebWorker uses pako.js to decompress the files.
Caching and Memory Management
Only the most recent five parsed models are kept in memory using an LRU strategy. All models are also stored in IndexedDB; the loading flow checks memory first, then IndexedDB, and finally falls back to a network request if needed.
// Example LRU loading logic
if (models[currentMedalInfo.id]) {
scene.add(models[currentMedalInfo.id]);
// update LRU list
modelsList.splice(modelsList.findIndex(id => currentMedalInfo.id === id), 1);
modelsList.push(currentMedalInfo.id);
resolve();
return;
}
// ... IndexedDB retrieval and fallback loading ...Graceful Degradation for Old Devices
When WebGL is unavailable, the system returns two static images (earned and unearned medal) and displays them as a fallback.
Enhanced User Experience
Additional gravity‑sensor interaction and particle effects were added to make the medal animation more engaging.
Final Visuals
Animated GIFs demonstrate the rotating 3D medal, skin changes, and particle effects.
Conclusion
With the arrival of 5G, network transmission and resource loading become faster, making WebGL an attractive cross‑platform solution for 3D effects. Using Three.js accelerates development, offering flexible options to meet both user experience goals and business requirements.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
