1.7K tris0.21 × 0.15 × 0.80 m
Use it in code
Remix in the editorskateboard.js · 286 lines
Self-contained ES module — only depends on three. Call the factory and add the group to your scene.
/**
* Skateboard — Codels 3D model library
* License: CC0-1.0 · Units: meters · Y-up · Front: +Z · Origin: center of the base (y = 0)
* Triangles: 1,700 · Size (W × H × D): 0.21 × 0.15 × 0.80 m
*
* Usage:
* import { createSkateboard } from './skateboard.js';
*
* const model = createSkateboard();
* model.position.set(0, 0, 0);
* scene.add(model);
*/
import * as THREE from 'three';
const deg = THREE.MathUtils.degToRad;
// Revolve a 2D profile [[radius, y], ...] around the Y axis.
function lathe(points, segments, phiStart = 0, phiLength = Math.PI * 2) {
return new THREE.LatheGeometry(points.map(([x, y]) => new THREE.Vector2(x, y)), segments, phiStart, phiLength);
}
// Extrude a 2D outline along Z (centered). bevel = [thickness, size, segments].
function extrude(outline, depth, holes = [], bevel = null) {
const shape = new THREE.Shape(outline.map(([x, y]) => new THREE.Vector2(x, y)));
for (const hole of holes) shape.holes.push(new THREE.Path(hole.map(([x, y]) => new THREE.Vector2(x, y))));
const options = bevel
? { depth, bevelEnabled: true, bevelThickness: bevel[0], bevelSize: bevel[1], bevelSegments: bevel[2] }
: { depth, bevelEnabled: false };
return new THREE.ExtrudeGeometry(shape, options).translate(0, 0, -depth / 2);
}
export function createSkateboard() {
const root = new THREE.Group();
root.name = 'Skateboard';
// Materials
const deckMat = new THREE.MeshStandardMaterial({ name: 'deck', color: 0xe0533d, roughness: 0.6, flatShading: true });
const plyMat = new THREE.MeshStandardMaterial({ name: 'ply', color: 0xd8b98a, roughness: 0.8, flatShading: true });
const gripMat = new THREE.MeshStandardMaterial({ name: 'grip', color: 0x1f2023, roughness: 1, flatShading: true });
const metalMat = new THREE.MeshStandardMaterial({ name: 'metal', color: 0xb4b9bf, roughness: 0.35, metalness: 0.7, flatShading: true });
const darkMat = new THREE.MeshStandardMaterial({ name: 'dark', color: 0x3a3c40, roughness: 0.6, metalness: 0.3, flatShading: true });
const bushingMat = new THREE.MeshStandardMaterial({ name: 'bushing', color: 0xf2c21b, roughness: 0.7, flatShading: true });
const wheelMat = new THREE.MeshStandardMaterial({ name: 'wheel', color: 0xf2ead2, roughness: 0.55, flatShading: true });
// Shared geometries
const boxGeo = new THREE.BoxGeometry(0.0785, 0.002, 0.472);
const boxGeo2 = new THREE.BoxGeometry(0.175, 0.001, 0.025);
const boxGeo3 = new THREE.BoxGeometry(0.058, 0.01, 0.088);
const boxGeo4 = new THREE.BoxGeometry(0.007, 0.002, 0.007);
const cylinderGeo = new THREE.CylinderGeometry(0.004, 0.004, 0.045, 6);
const cylinderGeo2 = new THREE.CylinderGeometry(0.011, 0.012, 0.012, 8);
const cylinderGeo3 = new THREE.CylinderGeometry(0.006, 0.006, 0.02, 6);
const extrudeGeo = extrude([[-0.066, -0.008], [0.066, -0.008], [0.03, 0.026], [-0.03, 0.026]], 0.024, [], [0.002, 0.002, 1]);
const cylinderGeo4 = new THREE.CylinderGeometry(0.004, 0.004, 0.2, 6);
const latheGeo = lathe([[0.0135, -0.012], [0.0135, -0.016], [0.02, -0.016], [0.027, -0.009], [0.027, 0.009], [0.02, 0.016], [0.0135, 0.016], [0.0135, 0.012]], 14);
const cylinderGeo5 = new THREE.CylinderGeometry(0.0135, 0.0135, 0.024, 10);
const cylinderGeo6 = new THREE.CylinderGeometry(0.006, 0.006, 0.01, 6);
// Parts
const body = new THREE.Group();
body.name = 'body';
root.add(body);
const deck = new THREE.Mesh(new THREE.BoxGeometry(0.205, 0.012, 0.47), deckMat);
deck.name = 'deck';
deck.position.set(0, 0.089, 0);
body.add(deck);
const plyEdge = new THREE.Mesh(new THREE.BoxGeometry(0.207, 0.003, 0.47), plyMat);
plyEdge.name = 'plyEdge';
plyEdge.position.set(0, 0.0845, 0);
body.add(plyEdge);
const nose = new THREE.Group();
nose.name = 'nose';
nose.position.set(0, 0.089, 0.235);
nose.rotation.set(deg(-19), 0, 0);
body.add(nose);
const noseDeck = new THREE.Mesh(extrude([[-0.1025, 0], [0.1025, 0], [0.1025, 0.07], [0.0975, 0.1017], [0.0829, 0.1302], [0.0602, 0.1529], [0.0317, 0.1675], [0, 0.1725], [-0.0317, 0.1675], [-0.0602, 0.1529], [-0.0829, 0.1302], [-0.0975, 0.1017], [-0.1025, 0.07]], 0.012), deckMat);
noseDeck.name = 'noseDeck';
noseDeck.rotation.set(deg(90), 0, 0);
nose.add(noseDeck);
const noseGrip = new THREE.Mesh(extrude([[-0.0945, 0], [0.0945, 0], [0.0945, 0.07], [0.0899, 0.0992], [0.0765, 0.1255], [0.0555, 0.1465], [0.0292, 0.1599], [0, 0.1645], [-0.0292, 0.1599], [-0.0555, 0.1465], [-0.0765, 0.1255], [-0.0899, 0.0992], [-0.0945, 0.07]], 0.002), gripMat);
noseGrip.name = 'noseGrip';
noseGrip.position.set(0, 0.007, 0.002);
noseGrip.rotation.set(deg(90), 0, 0);
nose.add(noseGrip);
const tail = new THREE.Group();
tail.name = 'tail';
tail.position.set(0, 0.089, -0.235);
tail.rotation.set(deg(19), 0, 0);
body.add(tail);
const tailDeck = new THREE.Mesh(extrude([[-0.1025, 0], [0.1025, 0], [0.1025, -0.07], [0.0975, -0.1017], [0.0829, -0.1302], [0.0602, -0.1529], [0.0317, -0.1675], [0, -0.1725], [-0.0317, -0.1675], [-0.0602, -0.1529], [-0.0829, -0.1302], [-0.0975, -0.1017], [-0.1025, -0.07]], 0.012), deckMat);
tailDeck.name = 'tailDeck';
tailDeck.rotation.set(deg(90), 0, 0);
tail.add(tailDeck);
const tailGrip = new THREE.Mesh(extrude([[-0.0945, 0], [0.0945, 0], [0.0945, -0.07], [0.0899, -0.0992], [0.0765, -0.1255], [0.0555, -0.1465], [0.0292, -0.1599], [0, -0.1645], [-0.0292, -0.1599], [-0.0555, -0.1465], [-0.0765, -0.1255], [-0.0899, -0.0992], [-0.0945, -0.07]], 0.002), gripMat);
tailGrip.name = 'tailGrip';
tailGrip.position.set(0, 0.007, -0.002);
tailGrip.rotation.set(deg(90), 0, 0);
tail.add(tailGrip);
const griptapeL = new THREE.Mesh(boxGeo, gripMat);
griptapeL.name = 'griptapeL';
griptapeL.position.set(0.0553, 0.096, 0);
body.add(griptapeL);
const griptapeR = new THREE.Mesh(boxGeo, gripMat);
griptapeR.name = 'griptapeR';
griptapeR.position.set(-0.0552, 0.096, 0);
body.add(griptapeR);
const box12 = new THREE.Mesh(boxGeo2, plyMat);
box12.position.set(0, 0.0825, -0.06);
body.add(box12);
const box13 = new THREE.Mesh(boxGeo2, plyMat);
box13.position.set(0, 0.0825, 0);
body.add(box13);
const box14 = new THREE.Mesh(boxGeo2, plyMat);
box14.position.set(0, 0.0825, 0.06);
body.add(box14);
const truckFront = new THREE.Group();
truckFront.name = 'truckFront';
truckFront.position.set(0, 0, 0.2);
body.add(truckFront);
const baseplate = new THREE.Mesh(boxGeo3, metalMat);
baseplate.name = 'baseplate';
baseplate.position.set(0, 0.078, 0);
truckFront.add(baseplate);
const box17 = new THREE.Mesh(boxGeo4, metalMat);
box17.position.set(0.02, 0.098, 0.03);
truckFront.add(box17);
const box18 = new THREE.Mesh(boxGeo4, metalMat);
box18.position.set(0.02, 0.098, -0.03);
truckFront.add(box18);
const box19 = new THREE.Mesh(boxGeo4, metalMat);
box19.position.set(-0.02, 0.098, 0.03);
truckFront.add(box19);
const box20 = new THREE.Mesh(boxGeo4, metalMat);
box20.position.set(-0.02, 0.098, -0.03);
truckFront.add(box20);
const kingpin = new THREE.Mesh(cylinderGeo, metalMat);
kingpin.name = 'kingpin';
kingpin.position.set(0, 0.057, -0.012);
kingpin.rotation.set(deg(35), 0, 0);
truckFront.add(kingpin);
const bushing = new THREE.Mesh(cylinderGeo2, bushingMat);
bushing.name = 'bushing';
bushing.position.set(0, 0.063, -0.009);
bushing.rotation.set(deg(35), 0, 0);
truckFront.add(bushing);
const pivot = new THREE.Mesh(cylinderGeo3, darkMat);
pivot.name = 'pivot';
pivot.position.set(0, 0.065, 0.022);
pivot.rotation.set(deg(-50), 0, 0);
truckFront.add(pivot);
const hanger = new THREE.Mesh(extrudeGeo, metalMat);
hanger.name = 'hanger';
hanger.position.set(0, 0.027, 0);
truckFront.add(hanger);
const axle = new THREE.Mesh(cylinderGeo4, metalMat);
axle.name = 'axle';
axle.position.set(0, 0.027, 0);
axle.rotation.set(0, 0, deg(90));
truckFront.add(axle);
const truckRear = new THREE.Group();
truckRear.name = 'truckRear';
truckRear.position.set(0, 0, -0.2);
body.add(truckRear);
const baseplate2 = new THREE.Mesh(boxGeo3, metalMat);
baseplate2.name = 'baseplate';
baseplate2.position.set(0, 0.078, 0);
truckRear.add(baseplate2);
const box28 = new THREE.Mesh(boxGeo4, metalMat);
box28.position.set(0.02, 0.098, 0.03);
truckRear.add(box28);
const box29 = new THREE.Mesh(boxGeo4, metalMat);
box29.position.set(0.02, 0.098, -0.03);
truckRear.add(box29);
const box30 = new THREE.Mesh(boxGeo4, metalMat);
box30.position.set(-0.02, 0.098, 0.03);
truckRear.add(box30);
const box31 = new THREE.Mesh(boxGeo4, metalMat);
box31.position.set(-0.02, 0.098, -0.03);
truckRear.add(box31);
const kingpin2 = new THREE.Mesh(cylinderGeo, metalMat);
kingpin2.name = 'kingpin';
kingpin2.position.set(0, 0.057, 0.012);
kingpin2.rotation.set(deg(-35), 0, 0);
truckRear.add(kingpin2);
const bushing2 = new THREE.Mesh(cylinderGeo2, bushingMat);
bushing2.name = 'bushing';
bushing2.position.set(0, 0.063, 0.009);
bushing2.rotation.set(deg(-35), 0, 0);
truckRear.add(bushing2);
const pivot2 = new THREE.Mesh(cylinderGeo3, darkMat);
pivot2.name = 'pivot';
pivot2.position.set(0, 0.065, -0.022);
pivot2.rotation.set(deg(50), 0, 0);
truckRear.add(pivot2);
const hanger2 = new THREE.Mesh(extrudeGeo, metalMat);
hanger2.name = 'hanger';
hanger2.position.set(0, 0.027, 0);
truckRear.add(hanger2);
const axle2 = new THREE.Mesh(cylinderGeo4, metalMat);
axle2.name = 'axle';
axle2.position.set(0, 0.027, 0);
axle2.rotation.set(0, 0, deg(90));
truckRear.add(axle2);
const wheels = new THREE.Group();
wheels.name = 'wheels';
root.add(wheels);
const wheelFL = new THREE.Group();
wheelFL.name = 'wheelFL';
wheelFL.position.set(0.084, 0.027, 0.2);
wheels.add(wheelFL);
const urethane = new THREE.Mesh(latheGeo, wheelMat);
urethane.name = 'urethane';
urethane.rotation.set(0, 0, deg(90));
wheelFL.add(urethane);
const bearing = new THREE.Mesh(cylinderGeo5, metalMat);
bearing.name = 'bearing';
bearing.rotation.set(0, 0, deg(90));
wheelFL.add(bearing);
const axleNut = new THREE.Mesh(cylinderGeo6, metalMat);
axleNut.name = 'axleNut';
axleNut.position.set(0.016, 0, 0);
axleNut.rotation.set(0, 0, deg(90));
wheelFL.add(axleNut);
const wheelFR = new THREE.Group();
wheelFR.name = 'wheelFR';
wheelFR.position.set(-0.084, 0.027, 0.2);
wheels.add(wheelFR);
const urethane2 = new THREE.Mesh(latheGeo, wheelMat);
urethane2.name = 'urethane';
urethane2.rotation.set(0, 0, deg(90));
wheelFR.add(urethane2);
const bearing2 = new THREE.Mesh(cylinderGeo5, metalMat);
bearing2.name = 'bearing';
bearing2.rotation.set(0, 0, deg(90));
wheelFR.add(bearing2);
const axleNut2 = new THREE.Mesh(cylinderGeo6, metalMat);
axleNut2.name = 'axleNut';
axleNut2.position.set(-0.016, 0, 0);
axleNut2.rotation.set(0, 0, deg(90));
wheelFR.add(axleNut2);
const wheelRL = new THREE.Group();
wheelRL.name = 'wheelRL';
wheelRL.position.set(0.084, 0.027, -0.2);
wheels.add(wheelRL);
const urethane3 = new THREE.Mesh(latheGeo, wheelMat);
urethane3.name = 'urethane';
urethane3.rotation.set(0, 0, deg(90));
wheelRL.add(urethane3);
const bearing3 = new THREE.Mesh(cylinderGeo5, metalMat);
bearing3.name = 'bearing';
bearing3.rotation.set(0, 0, deg(90));
wheelRL.add(bearing3);
const axleNut3 = new THREE.Mesh(cylinderGeo6, metalMat);
axleNut3.name = 'axleNut';
axleNut3.position.set(0.016, 0, 0);
axleNut3.rotation.set(0, 0, deg(90));
wheelRL.add(axleNut3);
const wheelRR = new THREE.Group();
wheelRR.name = 'wheelRR';
wheelRR.position.set(-0.084, 0.027, -0.2);
wheels.add(wheelRR);
const urethane4 = new THREE.Mesh(latheGeo, wheelMat);
urethane4.name = 'urethane';
urethane4.rotation.set(0, 0, deg(90));
wheelRR.add(urethane4);
const bearing4 = new THREE.Mesh(cylinderGeo5, metalMat);
bearing4.name = 'bearing';
bearing4.rotation.set(0, 0, deg(90));
wheelRR.add(bearing4);
const axleNut4 = new THREE.Mesh(cylinderGeo6, metalMat);
axleNut4.name = 'axleNut';
axleNut4.position.set(-0.016, 0, 0);
axleNut4.rotation.set(0, 0, deg(90));
wheelRR.add(axleNut4);
root.traverse((o) => {
if (o.isMesh) {
o.castShadow = true;
o.receiveShadow = true;
}
});
return root;
}
export default createSkateboard;
Ratings & comments
0 comments
No comments yet. Share how you used this model or what could be better.
Related models
Same category or similar tags









