Test Configurator
LEEFORDSleefords.it
LEEFORDS Soccer Kit Configurator V8 · leefords.it
Design your bespoke football kit in 2D, add player details and your club/sponsor logo, then send the specification to LEEFORDS.
Jersey
Shorts
Socks
Choosing a lower or upper colour switches from a preset to independent sock layers.
Personalisation
Your logo stays in your browser; it is not uploaded anywhere by this configurator.
Your enquiry
Order hand-off
Use “Copy Specification” to copy the design details, “Email LEEFORDS” to open a ready-addressed enquiry, or “Order & Contacts” to continue on leefords.it.
import * as THREE from "three";
import {
OrbitControls
} from "three/addons/controls/OrbitControls.js";
import {
GLTFLoader
} from "three/addons/loaders/GLTFLoader.js";
/* =========================================
MODEL LOCATION
========================================= */
const MODEL_URL =
"./assets/avatar/M_Avatar.glb";
/* =========================================
HTML
========================================= */
const canvas =
document.getElementById("viewer");
const status =
document.getElementById("status");
/* =========================================
SCENE
========================================= */
const scene =
new THREE.Scene();
scene.background =
new THREE.Color(0xeeeeee);
/* =========================================
CAMERA
========================================= */
const camera =
new THREE.PerspectiveCamera(
35,
window.innerWidth / window.innerHeight,
0.001,
100000
);
/* =========================================
RENDERER
========================================= */
const renderer =
new THREE.WebGLRenderer({
canvas: canvas,
antialias: true
});
renderer.setPixelRatio(
Math.min(
window.devicePixelRatio,
2
)
);
renderer.setSize(
window.innerWidth,
window.innerHeight
);
renderer.outputColorSpace =
THREE.SRGBColorSpace;
renderer.toneMapping =
THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure =
1.2;
/* =========================================
LIGHTS
========================================= */
const ambient =
new THREE.HemisphereLight(
0xffffff,
0x555555,
3
);
scene.add(ambient);
const frontLight =
new THREE.DirectionalLight(
0xffffff,
4
);
frontLight.position.set(
3,
5,
5
);
scene.add(frontLight);
const sideLight =
new THREE.DirectionalLight(
0xffffff,
2
);
sideLight.position.set(
-4,
3,
2
);
scene.add(sideLight);
/* =========================================
CONTROLS
========================================= */
const controls =
new OrbitControls(
camera,
renderer.domElement
);
controls.enableDamping = true;
controls.dampingFactor = 0.06;
controls.enablePan = false;
/* =========================================
LOAD CLO3D GLB
========================================= */
const loader =
new GLTFLoader();
loader.load(
MODEL_URL,
/* SUCCESS */
function (gltf) {
console.log(
"CLO3D model loaded:",
gltf
);
const model =
gltf.scene;
scene.add(model);
/* -----------------------------
Calculate REAL CLO dimensions
----------------------------- */
model.updateMatrixWorld(true);
const box =
new THREE.Box3()
.setFromObject(model);
const center =
box.getCenter(
new THREE.Vector3()
);
const size =
box.getSize(
new THREE.Vector3()
);
console.log(
"CLO model center:",
center
);
console.log(
"CLO model size:",
size
);
/* -----------------------------
Automatically position camera
----------------------------- */
const maxDimension =
Math.max(
size.x,
size.y,
size.z
);
const fov =
THREE.MathUtils.degToRad(
camera.fov
);
let distance =
maxDimension /
(
2 *
Math.tan(
fov / 2
)
);
distance *= 1.35;
camera.position.set(
center.x,
center.y,
center.z + distance
);
camera.near =
Math.max(
distance / 1000,
0.001
);
camera.far =
Math.max(
distance * 100,
1000
);
camera.updateProjectionMatrix();
controls.target.copy(
center
);
controls.update();
status.textContent =
"3D model loaded";
/*
Hide message after two seconds.
*/
setTimeout(
() => {
status.style.display =
"none";
},
2000
);
},
/* PROGRESS */
function (event) {
if (
event.total &&
event.total > 0
) {
const percent =
Math.round(
event.loaded /
event.total *
100
);
status.textContent =
"Loading 3D model: " +
percent +
"%";
}
},
/* ERROR */
function (error) {
console.error(
"GLB LOAD ERROR:",
error
);
console.error(
"Attempted URL:",
MODEL_URL
);
status.textContent =
"ERROR: 3D model could not be loaded.";
status.style.background =
"#b00020";
}
);
/* =========================================
RESIZE
========================================= */
window.addEventListener(
"resize",
function () {
camera.aspect =
window.innerWidth /
window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(
window.innerWidth,
window.innerHeight
);
}
);
/* =========================================
RENDER
========================================= */
function animate() {
requestAnimationFrame(
animate
);
controls.update();
renderer.render(
scene,
camera
);
}
animate();/*
=============================================================
LEEFORDS 3D CONFIGURATOR
ASSET LOCATION CONFIGURATION
=============================================================
*/
/*
-------------------------------------------------------------
1. WEBSITE ROOT
-------------------------------------------------------------
This is the public address of the website.
Do NOT put index.html on the end.
*/
const WEBSITE_ROOT =
"https://leefords.it";
/*
-------------------------------------------------------------
2. MASTER ASSET DIRECTORY
-------------------------------------------------------------
ALL configurator assets will be loaded from:
https://leefords.it/assets/
This includes:
- CLO3D avatar
- CLO3D garments
- garment colorways
- thumbnail images
*/
const ASSET_ROOT =
WEBSITE_ROOT + "/assets";
/*
-------------------------------------------------------------
3. ASSET SUBDIRECTORIES
-------------------------------------------------------------
*/
const AVATAR_ROOT =
ASSET_ROOT + "/avatar";
const GARMENT_ROOT =
ASSET_ROOT + "/garments";
const THUMB_ROOT =
ASSET_ROOT + "/thumbs";
/*
=============================================================
4. COMPLETE PRODUCT CATALOGUE
=============================================================
*/
const catalogue = {
/*
---------------------------------------------------------
AVATAR
---------------------------------------------------------
*/
avatar: {
id:
"male-avatar",
name:
"Male Avatar",
/*
CLO3D GLB
*/
model:
AVATAR_ROOT +
"/M_Avatar.glb",
/*
Avatar thumbnail
*/
thumb:
THUMB_ROOT +
"/Thumb_M Avatar_Black.png"
},
/*
---------------------------------------------------------
GARMENTS
---------------------------------------------------------
*/
garments: [
/*
=====================================================
VARSITY JACKET
=====================================================
*/
{
id:
"varsity-jacket",
name:
"Varsity Jacket",
slot:
"upper",
/*
Main garment thumbnail
*/
thumb:
THUMB_ROOT +
"/Thumb_Varsity Jacket_Black.png",
variants: [
/*
BLACK
*/
{
id:
"black",
name:
"Black",
model:
GARMENT_ROOT +
"/varsity-jacket/" +
"Varsity Jacket_Black.glb",
thumb:
THUMB_ROOT +
"/Thumb_Varsity Jacket_Black.png"
},
/*
BLUE / COLORWAY 1
*/
{
id:
"blue",
name:
"Blue",
model:
GARMENT_ROOT +
"/varsity-jacket/" +
"Varsity Jacket_Colorway 1.glb",
thumb:
THUMB_ROOT +
"/Thumb_Varsity Jacket_Colorway 1.png"
},
/*
NAVY
*/
{
id:
"navy",
name:
"Navy",
model:
GARMENT_ROOT +
"/varsity-jacket/" +
"Varsity Jacket_Navy.glb",
thumb:
THUMB_ROOT +
"/Thumb_Varsity Jacket_Navy.png"
},
/*
RED
*/
{
id:
"red",
name:
"Red",
model:
GARMENT_ROOT +
"/varsity-jacket/" +
"Varsity Jacket_Red.glb",
thumb:
THUMB_ROOT +
"/Thumb_Varsity Jacket_Red.png"
},
/*
YELLOW
*/
{
id:
"yellow",
name:
"Yellow",
model:
GARMENT_ROOT +
"/varsity-jacket/" +
"Varsity Jacket_Yellow.glb",
thumb:
THUMB_ROOT +
"/Thumb_Varsity Jacket_Yellow.png"
}
]
},
/*
=====================================================
SNAP PANTS
=====================================================
*/
{
id:
"snap-pants",
name:
"Snap Pants",
slot:
"lower",
/*
Main garment thumbnail
*/
thumb:
THUMB_ROOT +
"/Thumb_Snap Pant_Black.png",
variants: [
/*
BLACK
*/
{
id:
"black",
name:
"Black",
model:
GARMENT_ROOT +
"/snap-pants/" +
"Snap Pants_Black.glb",
thumb:
THUMB_ROOT +
"/Thumb_Snap Pant_Black.png"
},
/*
BLUE / COLORWAY 1
*/
{
id:
"blue",
name:
"Blue",
model:
GARMENT_ROOT +
"/snap-pants/" +
"Snap Pants_Colorway 1.glb",
thumb:
THUMB_ROOT +
"/Thumb_Snap Pant_Colorway 1.png"
},
/*
NAVY
*/
{
id:
"navy",
name:
"Navy",
model:
GARMENT_ROOT +
"/snap-pants/" +
"Snap Pants_Navy.glb",
thumb:
THUMB_ROOT +
"/Thumb_Snap Pant_Navy.png"
},
/*
RED
*/
{
id:
"red",
name:
"Red",
model:
GARMENT_ROOT +
"/snap-pants/" +
"Snap Pants_Red.glb",
thumb:
THUMB_ROOT +
"/Thumb_Snap Pant_Red.png"
},
/*
YELLOW
*/
{
id:
"yellow",
name:
"Yellow",
model:
GARMENT_ROOT +
"/snap-pants/" +
"Snap Pants_Yellow.glb",
thumb:
THUMB_ROOT +
"/Thumb_Snap Pant_Yellow.png"
}
]
}
]
};
REVISITING CLASSICS
Personal Protection Equipment
Inspired by Sportswear
America's Cup
Performance Wear Concepts.
ADIDAS MEN'S ATLANTA TRACKSUIT
ADIDAS MEN'S SITOR TRACKSUIT
ATLANTA 1996 Olympics Onepiece Suit_V2
MENS & WOMENS NORTON MOTORBIKE GEAR
NEO CLASSIC TRACKSUITS WITH HIGH VIS BANDS
COLOUR-BLOCK TRACKSUITS WITH HIGH VIS BANDS
2026 Winter Olympics BOBSLEIGH SUIT Proposal