# Wheel Crank

> A sturdy wheel crank on a bolted post for opening gates, raising bridges and solving puzzles: a steel gearbox with a side cog and a status lamp, and a spoked hand wheel with a bright rim and a turning knob. The wheel is the 'wheel' group with its origin on the axle, so Angle (or code) spins it around Z; the spoke count and wheel color are adjustable.

![Wheel Crank preview](https://codels.download/m/crank.png)

Multi-view sheet (3/4, front, side, top): https://codels.download/m/crank.sheet.png

## Facts

- Category: Game Kit › Interactive
- Size (W × H × D): 0.75 × 1.48 × 0.89 m — units are meters, +Y up, front faces +Z
- Origin: center of the base (y = 0)
- Triangles: 1,064 · Meshes: 26 · Materials: 7
- License: CC0-1.0 (Codels built-in)
- Tags: crank, valve, wheel, cartoon, low-poly, stylized, industrial, steampunk, dungeon, adventure, party-game, platformer, puzzle, interactable, metal, round, animated, emissive, game-ready, mobile-friendly, parametric, color-blue, color-orange, size-medium, poly-low
- Page: https://codels.download/models/crank

## Files

| Format | URL |
|---|---|
| three.js ES module (`createCrank()`) | https://codels.download/m/crank.js |
| React Three Fiber component (`<Crank />`) | https://codels.download/m/crank.jsx |
| Instancing module (`createCrankInstances(count, place)`, one draw call per material) | https://codels.download/m/crank.instanced.js |
| Standalone HTML demo | https://codels.download/m/crank.html |
| glTF binary | https://codels.download/m/crank.glb |
| glTF binary, Meshopt-compressed (2–3× smaller; GLTFLoader needs `setMeshoptDecoder(MeshoptDecoder)`) | https://codels.download/m/crank.glb?compress=meshopt |
| glTF JSON | https://codels.download/m/crank.gltf |
| OBJ (vertex colors) + MTL | https://codels.download/m/crank.obj · https://codels.download/m/crank.mtl |
| STL | https://codels.download/m/crank.stl |
| CodelSpec JSON | https://codels.download/m/crank.json |

## Parameters

Append them as query parameters to any URL above to get a customized variant (e.g. `?angle=...`).

| Param | Type | Range / options | Default | Current |
|---|---|---|---|---|
| `angle` | number | 0 – 360 | 30 | 30 |
| `spokes` | int | 3 – 8 | 4 | 4 |
| `color` | color | #rrggbb | #e8392f | #e8392f |

## Parts

- base (box · dark)
- baseTrim (box · hazard)
- post (box · frame)
- postCollar (box · dark)
- housing (box · frame)
- housingBand (box · hazard)
- housingLid (box · dark)
- lampBase (cylinder · dark)
- lamp (sphere · lamp)
- cog (extrude · steel)
- bearing (cylinder · dark)
- axle (cylinder · steel)
- wheel (group)
  - rim (torus · wheel)
  - hub (cylinder · wheel)
  - hubCap (cylinder · steel)
  - spoke1 (group)
  - spoke2 (group)
  - spoke3 (group)
  - spoke4 (group)
  - knob (capsule · grip)

## How to use

Save the code below as `crank.js`, then:

```js
import { createCrank } from './crank.js';
const model = createCrank();
scene.add(model);
```

## Code

```js
/**
 * Wheel Crank — Codels 3D model library (https://codels.download/models/crank)
 * License: CC0-1.0 · Units: meters · Y-up · Front: +Z · Origin: center of the base (y = 0)
 * Triangles: 1,064 · Size (W × H × D): 0.75 × 1.48 × 0.89 m
 *
 * Usage:
 *   import { createCrank } from './crank.js';
 *   
 *   const model = createCrank();
 *   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 createCrank() {
  const root = new THREE.Group();
  root.name = 'Crank';

  // Materials
  const frameMat = new THREE.MeshStandardMaterial({ name: 'frame', color: 0x3a4252, roughness: 0.6, metalness: 0.35, flatShading: true });
  const darkMat = new THREE.MeshStandardMaterial({ name: 'dark', color: 0x232831, roughness: 0.7, metalness: 0.3, flatShading: true });
  const steelMat = new THREE.MeshStandardMaterial({ name: 'steel', color: 0xc3cad4, roughness: 0.3, metalness: 0.75, flatShading: true });
  const hazardMat = new THREE.MeshStandardMaterial({ name: 'hazard', color: 0xf5c02a, roughness: 0.55, flatShading: true });
  const wheelMat = new THREE.MeshStandardMaterial({ name: 'wheel', color: 0xe8392f, roughness: 0.4, metalness: 0.2, flatShading: true });
  const gripMat = new THREE.MeshStandardMaterial({ name: 'grip', color: 0x2a2d33, roughness: 0.8, flatShading: true });
  const lampMat = new THREE.MeshStandardMaterial({ name: 'lamp', color: 0xc9ffd9, roughness: 0.3, emissive: 0x3dff7a, emissiveIntensity: 1.1, flatShading: true });

  // Shared geometries
  const cylinderGeo = new THREE.CylinderGeometry(0.028, 0.028, 0.03, 6);
  const boxGeo = new THREE.BoxGeometry(0.04, 0.28, 0.03);

  // Parts
  const base = new THREE.Mesh(new THREE.BoxGeometry(0.64, 0.08, 0.64), darkMat);
  base.name = 'base';
  base.position.set(0, 0.04, 0);
  root.add(base);
  const baseTrim = new THREE.Mesh(new THREE.BoxGeometry(0.56, 0.05, 0.56), hazardMat);
  baseTrim.name = 'baseTrim';
  baseTrim.position.set(0, 0.105, 0);
  root.add(baseTrim);
  const cylinder3 = new THREE.Mesh(cylinderGeo, steelMat);
  cylinder3.position.set(-0.23, 0.145, -0.23);
  root.add(cylinder3);
  const cylinder4 = new THREE.Mesh(cylinderGeo, steelMat);
  cylinder4.position.set(0.23, 0.145, -0.23);
  root.add(cylinder4);
  const cylinder5 = new THREE.Mesh(cylinderGeo, steelMat);
  cylinder5.position.set(0.23, 0.145, 0.23);
  root.add(cylinder5);
  const cylinder6 = new THREE.Mesh(cylinderGeo, steelMat);
  cylinder6.position.set(-0.23, 0.145, 0.23);
  root.add(cylinder6);
  const post = new THREE.Mesh(new THREE.BoxGeometry(0.22, 0.82, 0.22), frameMat);
  post.name = 'post';
  post.position.set(0, 0.54, 0);
  root.add(post);
  const postCollar = new THREE.Mesh(new THREE.BoxGeometry(0.26, 0.06, 0.26), darkMat);
  postCollar.name = 'postCollar';
  postCollar.position.set(0, 0.16, 0);
  root.add(postCollar);
  const housing = new THREE.Mesh(new THREE.BoxGeometry(0.42, 0.36, 0.32), frameMat);
  housing.name = 'housing';
  housing.position.set(0, 1.1, 0);
  root.add(housing);
  const housingBand = new THREE.Mesh(new THREE.BoxGeometry(0.44, 0.06, 0.34), hazardMat);
  housingBand.name = 'housingBand';
  housingBand.position.set(0, 0.98, 0);
  root.add(housingBand);
  const housingLid = new THREE.Mesh(new THREE.BoxGeometry(0.36, 0.04, 0.26), darkMat);
  housingLid.name = 'housingLid';
  housingLid.position.set(0, 1.3, 0);
  root.add(housingLid);
  const lampBase = new THREE.Mesh(new THREE.CylinderGeometry(0.05, 0.06, 0.04, 8), darkMat);
  lampBase.name = 'lampBase';
  lampBase.position.set(0.1, 1.34, 0);
  root.add(lampBase);
  const lamp = new THREE.Mesh(new THREE.SphereGeometry(0.045, 8, 6), lampMat);
  lamp.name = 'lamp';
  lamp.position.set(0.1, 1.38, 0);
  root.add(lamp);
  const cog = new THREE.Mesh(extrude([[0.13, 0], [0.1692, 0.016], [0.1617, 0.0525], [0.1193, 0.0516], [0.1052, 0.0764], [0.1275, 0.1124], [0.0999, 0.1375], [0.0662, 0.1119], [0.0402, 0.1236], [0.0371, 0.1659], [0, 0.17], [-0.0122, 0.1294], [-0.0402, 0.1236], [-0.0675, 0.156], [-0.0999, 0.1375], [-0.086, 0.0975], [-0.1052, 0.0764], [-0.1463, 0.0865], [-0.1617, 0.0525], [-0.1269, 0.0284], [-0.13, 0], [-0.1692, -0.016], [-0.1617, -0.0525], [-0.1193, -0.0516], [-0.1052, -0.0764], [-0.1275, -0.1124], [-0.0999, -0.1375], [-0.0662, -0.1119], [-0.0402, -0.1236], [-0.0371, -0.1659], [0, -0.17], [0.0122, -0.1294], [0.0402, -0.1236], [0.0675, -0.156], [0.0999, -0.1375], [0.086, -0.0975], [0.1052, -0.0764], [0.1463, -0.0865], [0.1617, -0.0525], [0.1269, -0.0284]], 0.04), steelMat);
  cog.name = 'cog';
  cog.position.set(0.23, 1.12, 0);
  cog.rotation.set(0, deg(90), 0);
  root.add(cog);
  const cylinder15 = new THREE.Mesh(new THREE.CylinderGeometry(0.05, 0.05, 0.06, 8), darkMat);
  cylinder15.position.set(0.25, 1.12, 0);
  cylinder15.rotation.set(0, 0, deg(90));
  root.add(cylinder15);
  const bearing = new THREE.Mesh(new THREE.CylinderGeometry(0.08, 0.09, 0.06, 10), darkMat);
  bearing.name = 'bearing';
  bearing.position.set(0, 1.1, 0.19);
  bearing.rotation.set(deg(90), 0, 0);
  root.add(bearing);
  const axle = new THREE.Mesh(new THREE.CylinderGeometry(0.04, 0.04, 0.16, 8), steelMat);
  axle.name = 'axle';
  axle.position.set(0, 1.1, 0.26);
  axle.rotation.set(deg(90), 0, 0);
  root.add(axle);
  const wheel = new THREE.Group();
  wheel.name = 'wheel';
  wheel.position.set(0, 1.1, 0.33);
  wheel.rotation.set(0, 0, deg(-30));
  root.add(wheel);
  const rim = new THREE.Mesh(new THREE.TorusGeometry(0.34, 0.035, 6, 24), wheelMat);
  rim.name = 'rim';
  wheel.add(rim);
  const hub = new THREE.Mesh(new THREE.CylinderGeometry(0.075, 0.075, 0.09, 10), wheelMat);
  hub.name = 'hub';
  hub.rotation.set(deg(90), 0, 0);
  wheel.add(hub);
  const hubCap = new THREE.Mesh(new THREE.CylinderGeometry(0.045, 0.045, 0.1, 8), steelMat);
  hubCap.name = 'hubCap';
  hubCap.position.set(0, 0, 0.02);
  hubCap.rotation.set(deg(90), 0, 0);
  wheel.add(hubCap);
  const spoke1 = new THREE.Group();
  spoke1.name = 'spoke1';
  wheel.add(spoke1);
  const box23 = new THREE.Mesh(boxGeo, wheelMat);
  box23.position.set(0, 0.2, 0);
  spoke1.add(box23);
  const spoke2 = new THREE.Group();
  spoke2.name = 'spoke2';
  spoke2.rotation.set(0, 0, deg(90));
  wheel.add(spoke2);
  const box25 = new THREE.Mesh(boxGeo, wheelMat);
  box25.position.set(0, 0.2, 0);
  spoke2.add(box25);
  const spoke3 = new THREE.Group();
  spoke3.name = 'spoke3';
  spoke3.rotation.set(0, 0, deg(180));
  wheel.add(spoke3);
  const box27 = new THREE.Mesh(boxGeo, wheelMat);
  box27.position.set(0, 0.2, 0);
  spoke3.add(box27);
  const spoke4 = new THREE.Group();
  spoke4.name = 'spoke4';
  spoke4.rotation.set(0, 0, deg(270));
  wheel.add(spoke4);
  const box29 = new THREE.Mesh(boxGeo, wheelMat);
  box29.position.set(0, 0.2, 0);
  spoke4.add(box29);
  const cylinder30 = new THREE.Mesh(new THREE.CylinderGeometry(0.02, 0.02, 0.1, 6), steelMat);
  cylinder30.position.set(-0.2404, 0.2404, 0.06);
  cylinder30.rotation.set(deg(90), 0, 0);
  wheel.add(cylinder30);
  const knob = new THREE.Mesh(new THREE.CapsuleGeometry(0.032, 0.09, 2, 8), gripMat);
  knob.name = 'knob';
  knob.position.set(-0.2404, 0.2404, 0.16);
  knob.rotation.set(deg(90), 0, 0);
  wheel.add(knob);

  root.traverse((o) => {
    if (o.isMesh) {
      o.castShadow = true;
      o.receiveShadow = true;
    }
  });
  return root;
}

export default createCrank;
```

---
Codels — 3D models as code for three.js. Search more: https://codels.download/api/v1/models?q=<query> · Guide: https://codels.download/llms.txt
