1.1K tris0.34 × 0.22 × 0.28 m
Use it in code
Remix in the editorcrab.js · 291 lines
Self-contained ES module — only depends on three. Call the factory and add the group to your scene.
/**
* Crab — Codels 3D model library
* License: CC0-1.0 · Units: meters · Y-up · Front: +Z · Origin: center of the base (y = 0)
* Triangles: 1,060 · Size (W × H × D): 0.34 × 0.21 × 0.28 m
*
* Usage:
* import { createCrab } from './crab.js';
*
* const model = createCrab();
* model.position.set(0, 0, 0);
* scene.add(model);
*/
import * as THREE from 'three';
const deg = THREE.MathUtils.degToRad;
// 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 createCrab() {
const root = new THREE.Group();
root.name = 'Crab';
// Materials
const shellMat = new THREE.MeshStandardMaterial({ name: 'shell', color: 0xe2482f, roughness: 0.45, flatShading: true });
const darkMat = new THREE.MeshStandardMaterial({ name: 'dark', color: 0xa33422, roughness: 0.5, flatShading: true });
const bellyMat = new THREE.MeshStandardMaterial({ name: 'belly', color: 0xf7c2ac, roughness: 0.6, flatShading: true });
const eyeWhiteMat = new THREE.MeshStandardMaterial({ name: 'eyeWhite', color: 0xfbfaf6, roughness: 0.3, flatShading: true });
const pupilMat = new THREE.MeshStandardMaterial({ name: 'pupil', color: 0x161317, roughness: 0.25, flatShading: true });
// Shared geometries
const cylinderGeo = new THREE.CylinderGeometry(0.009, 0.012, 0.08, 5);
const sphereGeo = new THREE.SphereGeometry(0.027, 8, 6);
const sphereGeo2 = new THREE.SphereGeometry(0.014, 6, 4);
const boxGeo = new THREE.BoxGeometry(0.006, 0.006, 0.003);
const boxGeo2 = new THREE.BoxGeometry(0.028, 0.1257, 0.028);
const boxGeo3 = new THREE.BoxGeometry(0.024, 0.1526, 0.024);
const boxGeo4 = new THREE.BoxGeometry(0.028, 0.1261, 0.028);
const boxGeo5 = new THREE.BoxGeometry(0.024, 0.153, 0.024);
const boxGeo6 = new THREE.BoxGeometry(0.028, 0.1275, 0.028);
const boxGeo7 = new THREE.BoxGeometry(0.024, 0.1578, 0.024);
const boxGeo8 = new THREE.BoxGeometry(0.036, 0.119, 0.036);
const boxGeo9 = new THREE.BoxGeometry(0.04, 0.121, 0.04);
const extrudeGeo = extrude([[-0.035, -0.035], [0.04, -0.05], [0.11, -0.04], [0.16, -0.008], [0.12, -0.012], [0.075, -0.005], [0.05, 0.03], [0.01, 0.048], [-0.03, 0.035]], 0.034, [], [0.008, 0.008, 1]);
const extrudeGeo2 = extrude([[0, -0.012], [0.04, 0.012], [0.085, 0.012], [0.115, -0.012], [0.075, -0.004], [0.035, -0.014]], 0.026, [], [0.008, 0.008, 1]);
// Parts
const crab = new THREE.Group();
crab.name = 'crab';
crab.scale.setScalar(0.6);
root.add(crab);
const body = new THREE.Group();
body.name = 'body';
crab.add(body);
const shell = new THREE.Mesh(new THREE.SphereGeometry(1, 12, 7), shellMat);
shell.name = 'shell';
shell.position.set(0, 0.09, 0);
shell.scale.set(0.15, 0.068, 0.115);
body.add(shell);
const underside = new THREE.Mesh(new THREE.SphereGeometry(1, 12, 5), bellyMat);
underside.name = 'underside';
underside.position.set(0, 0.068, 0.004);
underside.scale.set(0.145, 0.05, 0.11);
body.add(underside);
const shellSpot = new THREE.Mesh(new THREE.SphereGeometry(0.022, 6, 3), darkMat);
shellSpot.name = 'shellSpot';
shellSpot.position.set(0.06, 0.1457, -0.03);
shellSpot.scale.set(1, 0.3, 1);
body.add(shellSpot);
const shellSpot2 = new THREE.Mesh(new THREE.SphereGeometry(0.018, 6, 3), darkMat);
shellSpot2.name = 'shellSpot';
shellSpot2.position.set(-0.07, 0.145, -0.02);
shellSpot2.scale.set(1, 0.3, 1);
body.add(shellSpot2);
const shellSpot3 = new THREE.Mesh(new THREE.SphereGeometry(0.016, 6, 3), darkMat);
shellSpot3.name = 'shellSpot';
shellSpot3.position.set(0, 0.1421, -0.065);
shellSpot3.scale.set(1, 0.3, 1);
body.add(shellSpot3);
const shellSpot4 = new THREE.Mesh(new THREE.SphereGeometry(0.014, 6, 3), darkMat);
shellSpot4.name = 'shellSpot';
shellSpot4.position.set(-0.02, 0.151, 0.03);
shellSpot4.scale.set(1, 0.3, 1);
body.add(shellSpot4);
const mouth = new THREE.Mesh(new THREE.BoxGeometry(0.04, 0.008, 0.008), pupilMat);
mouth.name = 'mouth';
mouth.position.set(0, 0.086, 0.112);
body.add(mouth);
const eyeStalkL = new THREE.Mesh(cylinderGeo, shellMat);
eyeStalkL.name = 'eyeStalkL';
eyeStalkL.position.set(0.042, 0.175, 0.07);
eyeStalkL.rotation.set(deg(10), 0, deg(-8));
body.add(eyeStalkL);
const eyeL = new THREE.Mesh(sphereGeo, eyeWhiteMat);
eyeL.name = 'eyeL';
eyeL.position.set(0.048, 0.22, 0.077);
body.add(eyeL);
const pupilL = new THREE.Mesh(sphereGeo2, pupilMat);
pupilL.name = 'pupilL';
pupilL.position.set(0.046, 0.222, 0.1);
pupilL.scale.set(1, 1.15, 0.5);
body.add(pupilL);
const eyeShineL = new THREE.Mesh(boxGeo, eyeWhiteMat);
eyeShineL.name = 'eyeShineL';
eyeShineL.position.set(0.051, 0.228, 0.107);
body.add(eyeShineL);
const eyeStalkR = new THREE.Mesh(cylinderGeo, shellMat);
eyeStalkR.name = 'eyeStalkR';
eyeStalkR.position.set(-0.042, 0.175, 0.07);
eyeStalkR.rotation.set(deg(10), 0, deg(8));
body.add(eyeStalkR);
const eyeR = new THREE.Mesh(sphereGeo, eyeWhiteMat);
eyeR.name = 'eyeR';
eyeR.position.set(-0.048, 0.22, 0.077);
body.add(eyeR);
const pupilR = new THREE.Mesh(sphereGeo2, pupilMat);
pupilR.name = 'pupilR';
pupilR.position.set(-0.046, 0.222, 0.1);
pupilR.scale.set(1, 1.15, 0.5);
body.add(pupilR);
const eyeShineR = new THREE.Mesh(boxGeo, eyeWhiteMat);
eyeShineR.name = 'eyeShineR';
eyeShineR.position.set(-0.041, 0.228, 0.107);
body.add(eyeShineR);
const legL1 = new THREE.Group();
legL1.name = 'legL1';
legL1.position.set(0.11, 0.09, 0.035);
crab.add(legL1);
const legUpper = new THREE.Mesh(boxGeo2, shellMat);
legUpper.name = 'legUpper';
legUpper.position.set(0.05, 0.0225, 0);
legUpper.rotation.set(deg(-90), deg(-24.2277), deg(-90));
legL1.add(legUpper);
const legLower = new THREE.Mesh(boxGeo3, darkMat);
legLower.name = 'legLower';
legLower.position.set(0.1275, -0.0195, 0.005);
legLower.rotation.set(deg(-67.5692), deg(64.5221), deg(-114.5731));
legL1.add(legLower);
const legL2 = new THREE.Group();
legL2.name = 'legL2';
legL2.position.set(0.11, 0.09, -0.02);
crab.add(legL2);
const legUpper2 = new THREE.Mesh(boxGeo4, shellMat);
legUpper2.name = 'legUpper';
legUpper2.position.set(0.05, 0.0225, -0.005);
legUpper2.rotation.set(deg(-87.4489), deg(-23.994), deg(-83.7472));
legL2.add(legUpper2);
const legLower2 = new THREE.Mesh(boxGeo5, darkMat);
legLower2.name = 'legLower';
legLower2.position.set(0.1275, -0.0195, -0.0175);
legLower2.rotation.set(deg(-120.7689), deg(61.9374), deg(-55.9922));
legL2.add(legLower2);
const legL3 = new THREE.Group();
legL3.name = 'legL3';
legL3.position.set(0.11, 0.09, -0.07);
crab.add(legL3);
const legUpper3 = new THREE.Mesh(boxGeo6, shellMat);
legUpper3.name = 'legUpper';
legUpper3.position.set(0.05, 0.0225, -0.01);
legUpper3.rotation.set(deg(-85.054), deg(-23.32), deg(-77.6688));
legL3.add(legUpper3);
const legLower3 = new THREE.Mesh(boxGeo7, darkMat);
legLower3.name = 'legLower';
legLower3.position.set(0.1275, -0.0195, -0.04);
legLower3.rotation.set(deg(-138.1296), deg(45.6766), deg(-32.6693));
legL3.add(legLower3);
const legR1 = new THREE.Group();
legR1.name = 'legR1';
legR1.position.set(-0.11, 0.09, 0.035);
crab.add(legR1);
const legUpper4 = new THREE.Mesh(boxGeo2, shellMat);
legUpper4.name = 'legUpper';
legUpper4.position.set(-0.05, 0.0225, 0);
legUpper4.rotation.set(deg(-90), deg(24.2277), deg(90));
legR1.add(legUpper4);
const legLower4 = new THREE.Mesh(boxGeo3, darkMat);
legLower4.name = 'legLower';
legLower4.position.set(-0.1275, -0.0195, 0.005);
legLower4.rotation.set(deg(-67.5692), deg(-64.5221), deg(114.5731));
legR1.add(legLower4);
const legR2 = new THREE.Group();
legR2.name = 'legR2';
legR2.position.set(-0.11, 0.09, -0.02);
crab.add(legR2);
const legUpper5 = new THREE.Mesh(boxGeo4, shellMat);
legUpper5.name = 'legUpper';
legUpper5.position.set(-0.05, 0.0225, -0.005);
legUpper5.rotation.set(deg(-87.4489), deg(23.994), deg(83.7472));
legR2.add(legUpper5);
const legLower5 = new THREE.Mesh(boxGeo5, darkMat);
legLower5.name = 'legLower';
legLower5.position.set(-0.1275, -0.0195, -0.0175);
legLower5.rotation.set(deg(-120.7689), deg(-61.9374), deg(55.9922));
legR2.add(legLower5);
const legR3 = new THREE.Group();
legR3.name = 'legR3';
legR3.position.set(-0.11, 0.09, -0.07);
crab.add(legR3);
const legUpper6 = new THREE.Mesh(boxGeo6, shellMat);
legUpper6.name = 'legUpper';
legUpper6.position.set(-0.05, 0.0225, -0.01);
legUpper6.rotation.set(deg(-85.054), deg(23.32), deg(77.6688));
legR3.add(legUpper6);
const legLower6 = new THREE.Mesh(boxGeo7, darkMat);
legLower6.name = 'legLower';
legLower6.position.set(-0.1275, -0.0195, -0.04);
legLower6.rotation.set(deg(-138.1296), deg(-45.6766), deg(32.6693));
legR3.add(legLower6);
const clawL = new THREE.Group();
clawL.name = 'clawL';
clawL.position.set(0.11, 0.1, 0.07);
crab.add(clawL);
const clawArmL = new THREE.Mesh(boxGeo8, shellMat);
clawArmL.name = 'clawArmL';
clawArmL.position.set(0.04, 0.015, 0.025);
clawArmL.rotation.set(deg(-99.5667), deg(-14.8911), deg(-123.2585));
clawL.add(clawArmL);
const clawForearmL = new THREE.Mesh(boxGeo9, shellMat);
clawForearmL.name = 'clawForearmL';
clawForearmL.position.set(0.09, 0.065, 0.085);
clawForearmL.rotation.set(deg(-132.7543), deg(-10.9767), deg(-168.3624));
clawL.add(clawForearmL);
const pincerL = new THREE.Group();
pincerL.name = 'pincerL';
pincerL.position.set(0.1, 0.1, 0.12);
pincerL.rotation.set(deg(-38), deg(18), 0);
clawL.add(pincerL);
const clawPalmL = new THREE.Mesh(extrudeGeo, shellMat);
clawPalmL.name = 'clawPalmL';
clawPalmL.rotation.set(0, deg(-90), 0);
pincerL.add(clawPalmL);
const clawDactylL = new THREE.Group();
clawDactylL.name = 'clawDactylL';
clawDactylL.position.set(0, 0.03, 0.045);
clawDactylL.rotation.set(deg(-26), 0, 0);
pincerL.add(clawDactylL);
const clawTipL = new THREE.Mesh(extrudeGeo2, darkMat);
clawTipL.name = 'clawTipL';
clawTipL.rotation.set(0, deg(-90), 0);
clawDactylL.add(clawTipL);
const clawR = new THREE.Group();
clawR.name = 'clawR';
clawR.position.set(-0.11, 0.1, 0.07);
crab.add(clawR);
const clawArmR = new THREE.Mesh(boxGeo8, shellMat);
clawArmR.name = 'clawArmR';
clawArmR.position.set(-0.04, 0.015, 0.025);
clawArmR.rotation.set(deg(-99.5667), deg(14.8911), deg(123.2585));
clawR.add(clawArmR);
const clawForearmR = new THREE.Mesh(boxGeo9, shellMat);
clawForearmR.name = 'clawForearmR';
clawForearmR.position.set(-0.09, 0.065, 0.085);
clawForearmR.rotation.set(deg(-132.7543), deg(10.9767), deg(168.3624));
clawR.add(clawForearmR);
const pincerR = new THREE.Group();
pincerR.name = 'pincerR';
pincerR.position.set(-0.1, 0.1, 0.12);
pincerR.rotation.set(deg(-38), deg(-18), 0);
clawR.add(pincerR);
const clawPalmR = new THREE.Mesh(extrudeGeo, shellMat);
clawPalmR.name = 'clawPalmR';
clawPalmR.rotation.set(0, deg(-90), 0);
pincerR.add(clawPalmR);
const clawDactylR = new THREE.Group();
clawDactylR.name = 'clawDactylR';
clawDactylR.position.set(0, 0.03, 0.045);
clawDactylR.rotation.set(deg(-26), 0, 0);
pincerR.add(clawDactylR);
const clawTipR = new THREE.Mesh(extrudeGeo2, darkMat);
clawTipR.name = 'clawTipR';
clawTipR.rotation.set(0, deg(-90), 0);
clawDactylR.add(clawTipR);
root.traverse((o) => {
if (o.isMesh) {
o.castShadow = true;
o.receiveShadow = true;
}
});
return root;
}
export default createCrab;
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









