# Bucket

> A low-poly bucket in two builds: a tapered galvanized metal pail with a rolled rim, pressed ribs, riveted ears and a wire bail with a wooden grip, or a wooden stave bucket with iron hoops, two tall ear staves and a rope handle. Fill it with rippled water, raise the handle or let it drop to the side, and recolor the metal and wood. Perfect for farms, wells, stables, barns, docks and survival camps.

![Bucket preview](https://codels.download/m/bucket.png)

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

## Facts

- Category: Props › Containers
- Size (W × H × D): 0.32 × 0.45 × 0.31 m — units are meters, +Y up, front faces +Z
- Origin: center of the base (y = 0)
- Triangles: 808 · Meshes: 14 · Materials: 5
- License: CC0-1.0 (Codels built-in)
- Tags: bucket, pail, well, low-poly, stylized, camping, farm, medieval, village, farming-sim, rpg, survival, container, interactable, prop, tool, metal, wood, round, game-ready, instancing-friendly, mobile-friendly, parametric, color-gray, size-small, poly-low
- Page: https://codels.download/models/bucket

## Files

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

## Parameters

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

| Param | Type | Range / options | Default | Current |
|---|---|---|---|---|
| `style` | select | metal \| wood | metal | metal |
| `water` | boolean | true \| false | true | true |
| `handleUp` | boolean | true \| false | true | true |
| `metal` | color | #rrggbb | #a3abb3 | #a3abb3 |
| `wood` | color | #rrggbb | #a8743f | #a8743f |

## Parts

- body (lathe · metal)
- interior (lathe · metalDark)
- rolledRim (torus · metal)
- rib (lathe · metalDark)
- rib (lathe · metalDark)
- ear (box · metal)
- rivet (cylinder · metalDark)
- ear (box · metal)
- rivet (cylinder · metalDark)
- water (circle · water)
- ripple (ring · ripple)
- ripple (ring · ripple)
- handle (group)
  - bail (tube · metalDark)
  - grip (cylinder · wood)

## How to use

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

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

## Code

```js
/**
 * Bucket — Codels 3D model library (https://codels.download/models/bucket)
 * License: CC0-1.0 · Units: meters · Y-up · Front: +Z · Origin: center of the base (y = 0)
 * Triangles: 808 · Size (W × H × D): 0.32 × 0.45 × 0.31 m
 *
 * Usage:
 *   import { createBucket } from './bucket.js';
 *   
 *   const model = createBucket();
 *   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);
}

// Tube along a smooth curve through the given points.
function tube(path, radius, tubularSegments, radialSegments, closed = false) {
  const curve = new THREE.CatmullRomCurve3(path.map(([x, y, z]) => new THREE.Vector3(x, y, z)), closed);
  return new THREE.TubeGeometry(curve, tubularSegments, radius, radialSegments, closed);
}

export function createBucket() {
  const root = new THREE.Group();
  root.name = 'Bucket';

  // Materials
  const metalMat = new THREE.MeshStandardMaterial({ name: 'metal', color: 0xa3abb3, roughness: 0.4, metalness: 0.6, flatShading: true });
  const metalDarkMat = new THREE.MeshStandardMaterial({ name: 'metalDark', color: 0x72787d, roughness: 0.5, metalness: 0.5, flatShading: true });
  const woodMat = new THREE.MeshStandardMaterial({ name: 'wood', color: 0xa8743f, roughness: 0.85, flatShading: true });
  const waterMat = new THREE.MeshStandardMaterial({ name: 'water', color: 0x3f86b8, roughness: 0.12, metalness: 0.1 });
  const rippleMat = new THREE.MeshStandardMaterial({ name: 'ripple', color: 0x8cc3e6, roughness: 0.2, flatShading: true });

  // Shared geometries
  const boxGeo = new THREE.BoxGeometry(0.008, 0.06, 0.045);
  const cylinderGeo = new THREE.CylinderGeometry(0.008, 0.008, 0.01, 6);

  // Parts
  const body = new THREE.Mesh(lathe([[0, 0], [0.115, 0], [0.12, 0.008], [0.15, 0.3], [0.143, 0.3]], 16), metalMat);
  body.name = 'body';
  root.add(body);
  const interior = new THREE.Mesh(lathe([[0.143, 0.3], [0.114, 0.01], [0, 0.01]], 16), metalDarkMat);
  interior.name = 'interior';
  root.add(interior);
  const rolledRim = new THREE.Mesh(new THREE.TorusGeometry(0.1485, 0.0075, 4, 16), metalMat);
  rolledRim.name = 'rolledRim';
  rolledRim.position.set(0, 0.3, 0);
  rolledRim.rotation.set(deg(90), 0, 0);
  root.add(rolledRim);
  const rib = new THREE.Mesh(lathe([[0.1255, 0.075], [0.1315, 0.075], [0.1327, 0.087], [0.1267, 0.087]], 16), metalDarkMat);
  rib.name = 'rib';
  root.add(rib);
  const rib2 = new THREE.Mesh(lathe([[0.1385, 0.205], [0.1445, 0.205], [0.1457, 0.217], [0.1397, 0.217]], 16), metalDarkMat);
  rib2.name = 'rib';
  root.add(rib2);
  const ear = new THREE.Mesh(boxGeo, metalMat);
  ear.name = 'ear';
  ear.position.set(0.1492, 0.262, 0);
  root.add(ear);
  const rivet = new THREE.Mesh(cylinderGeo, metalDarkMat);
  rivet.name = 'rivet';
  rivet.position.set(0.1542, 0.262, 0);
  rivet.rotation.set(0, 0, deg(90));
  root.add(rivet);
  const ear2 = new THREE.Mesh(boxGeo, metalMat);
  ear2.name = 'ear';
  ear2.position.set(-0.1492, 0.262, 0);
  root.add(ear2);
  const rivet2 = new THREE.Mesh(cylinderGeo, metalDarkMat);
  rivet2.name = 'rivet';
  rivet2.position.set(-0.1542, 0.262, 0);
  rivet2.rotation.set(0, 0, deg(90));
  root.add(rivet2);
  const water = new THREE.Mesh(new THREE.CircleGeometry(0.1372, 16), waterMat);
  water.name = 'water';
  water.position.set(0, 0.222, 0);
  water.rotation.set(deg(-90), 0, 0);
  root.add(water);
  const ripple = new THREE.Mesh(new THREE.RingGeometry(0.035, 0.041, 12), rippleMat);
  ripple.name = 'ripple';
  ripple.position.set(0.03, 0.223, 0.02);
  ripple.rotation.set(deg(-90), 0, 0);
  root.add(ripple);
  const ripple2 = new THREE.Mesh(new THREE.RingGeometry(0.02, 0.026, 12), rippleMat);
  ripple2.name = 'ripple';
  ripple2.position.set(-0.05, 0.223, -0.03);
  ripple2.rotation.set(deg(-90), 0, 0);
  root.add(ripple2);
  const handle = new THREE.Group();
  handle.name = 'handle';
  handle.position.set(0, 0.262, 0);
  root.add(handle);
  const bail = new THREE.Mesh(tube([[-0.1582, 0, 0], [-0.1424, 0.0884, 0], [-0.0823, 0.153, 0], [0, 0.17, 0], [0.0823, 0.153, 0], [0.1424, 0.0884, 0], [0.1582, 0, 0]], 0.0035, 16, 4), metalDarkMat);
  bail.name = 'bail';
  handle.add(bail);
  const grip = new THREE.Mesh(new THREE.CylinderGeometry(0.014, 0.014, 0.1, 8), woodMat);
  grip.name = 'grip';
  grip.position.set(0, 0.17, 0);
  grip.rotation.set(0, 0, deg(90));
  handle.add(grip);

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

export default createBucket;
```

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