# Saguaro Cactus

> A cartoonish low-poly saguaro cactus: a tall ribbed column with rounded top and curved arms that bend upward, with optional pink blossoms on every tip. Set the height, number of arms and color to dress deserts, canyons, wild-west towns and Mexican-themed levels.

![Saguaro Cactus preview](https://codels.download/m/cactus.png)

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

## Facts

- Category: Nature › Trees
- Size (W × H × D): 1.84 × 3.61 × 0.62 m — units are meters, +Y up, front faces +Z
- Origin: center of the base (y = 0)
- Triangles: 876 · Meshes: 32 · Materials: 4
- License: CC0-1.0 (Codels built-in)
- Tags: cactus, saguaro, cartoon, low-poly, stylized, western, desert, adventure, platformer, racing-game, decoration, obstacle, vegetation, organic, tall, game-ready, mobile-friendly, parametric, color-green, size-large, poly-low
- Page: https://codels.download/models/cactus

## Files

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

## Parameters

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

| Param | Type | Range / options | Default | Current |
|---|---|---|---|---|
| `height` | number | 1.5 – 6 | 3.5 | 3.5 |
| `arms` | int | 0 – 4 | 2 | 2 |
| `color` | color | #rrggbb | #5a9350 | #5a9350 |
| `flowers` | boolean | true \| false | true | true |

## Parts

- column (cylinder · skin)
- top (sphere · skin)
- rib1 (box · rib)
- rib2 (box · rib)
- rib3 (box · rib)
- rib4 (box · rib)
- rib5 (box · rib)
- rib6 (box · rib)
- rib7 (box · rib)
- rib8 (box · rib)
- topFlower (group)
  - petal1 (octahedron · petal)
  - petal2 (octahedron · petal)
  - petal3 (octahedron · petal)
  - petal4 (octahedron · petal)
  - petal5 (octahedron · petal)
  - center (icosahedron · pollen)
- arm1 (tube · skin)
- armTip1 (sphere · skin)
- armFlower1 (group)
  - petal1 (octahedron · petal)
  - petal2 (octahedron · petal)
  - petal3 (octahedron · petal)
  - petal4 (octahedron · petal)
  - petal5 (octahedron · petal)
  - center (icosahedron · pollen)
- arm2 (tube · skin)
- armTip2 (sphere · skin)
- armFlower2 (group)
  - petal1 (octahedron · petal)
  - petal2 (octahedron · petal)
  - petal3 (octahedron · petal)
  - petal4 (octahedron · petal)
  - petal5 (octahedron · petal)
  - center (icosahedron · pollen)

## How to use

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

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

## Code

```js
/**
 * Saguaro Cactus — Codels 3D model library (https://codels.download/models/cactus)
 * License: CC0-1.0 · Units: meters · Y-up · Front: +Z · Origin: center of the base (y = 0)
 * Triangles: 876 · Size (W × H × D): 1.84 × 3.61 × 0.62 m
 *
 * Usage:
 *   import { createCactus } from './cactus.js';
 *   
 *   const model = createCactus();
 *   model.position.set(0, 0, 0);
 *   scene.add(model);
 */
import * as THREE from 'three';

const deg = THREE.MathUtils.degToRad;

// 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 createCactus() {
  const root = new THREE.Group();
  root.name = 'Cactus';

  // Materials
  const skinMat = new THREE.MeshStandardMaterial({ name: 'skin', color: 0x5a9350, roughness: 0.75, flatShading: true });
  const ribMat = new THREE.MeshStandardMaterial({ name: 'rib', color: 0x487640, roughness: 0.8, flatShading: true });
  const petalMat = new THREE.MeshStandardMaterial({ name: 'petal', color: 0xef6f96, roughness: 0.6, flatShading: true });
  const pollenMat = new THREE.MeshStandardMaterial({ name: 'pollen', color: 0xf6d14a, roughness: 0.6, flatShading: true });

  // Shared geometries
  const boxGeo = new THREE.BoxGeometry(0.0218, 2.9339, 0.0218);
  const octahedronGeo = new THREE.OctahedronGeometry(1, 0);
  const sphereGeo = new THREE.SphereGeometry(0.2053, 8, 3, 0, Math.PI * 2, 0, Math.PI / 2);
  const icosahedronGeo = new THREE.IcosahedronGeometry(0.0369, 0);

  // Parts
  const column = new THREE.Mesh(new THREE.CylinderGeometry(0.2923, 0.311, 3.189, 8), skinMat);
  column.name = 'column';
  column.position.set(0, 1.5945, 0);
  root.add(column);
  const top = new THREE.Mesh(new THREE.SphereGeometry(0.2923, 8, 3, 0, Math.PI * 2, 0, Math.PI / 2), skinMat);
  top.name = 'top';
  top.position.set(0, 3.189, 0);
  root.add(top);
  const rib1 = new THREE.Mesh(boxGeo, ribMat);
  rib1.name = 'rib1';
  rib1.position.set(0.1067, 1.4669, 0.2575);
  rib1.rotation.set(0, deg(22.5), 0);
  root.add(rib1);
  const rib2 = new THREE.Mesh(boxGeo, ribMat);
  rib2.name = 'rib2';
  rib2.position.set(0.2575, 1.4669, 0.1067);
  rib2.rotation.set(0, deg(67.5), 0);
  root.add(rib2);
  const rib3 = new THREE.Mesh(boxGeo, ribMat);
  rib3.name = 'rib3';
  rib3.position.set(0.2575, 1.4669, -0.1067);
  rib3.rotation.set(0, deg(112.5), 0);
  root.add(rib3);
  const rib4 = new THREE.Mesh(boxGeo, ribMat);
  rib4.name = 'rib4';
  rib4.position.set(0.1067, 1.4669, -0.2575);
  rib4.rotation.set(0, deg(157.5), 0);
  root.add(rib4);
  const rib5 = new THREE.Mesh(boxGeo, ribMat);
  rib5.name = 'rib5';
  rib5.position.set(-0.1067, 1.4669, -0.2575);
  rib5.rotation.set(0, deg(202.5), 0);
  root.add(rib5);
  const rib6 = new THREE.Mesh(boxGeo, ribMat);
  rib6.name = 'rib6';
  rib6.position.set(-0.2575, 1.4669, -0.1067);
  rib6.rotation.set(0, deg(247.5), 0);
  root.add(rib6);
  const rib7 = new THREE.Mesh(boxGeo, ribMat);
  rib7.name = 'rib7';
  rib7.position.set(-0.2575, 1.4669, 0.1067);
  rib7.rotation.set(0, deg(292.5), 0);
  root.add(rib7);
  const rib8 = new THREE.Mesh(boxGeo, ribMat);
  rib8.name = 'rib8';
  rib8.position.set(-0.1067, 1.4669, 0.2575);
  rib8.rotation.set(0, deg(337.5), 0);
  root.add(rib8);
  const topFlower = new THREE.Group();
  topFlower.name = 'topFlower';
  topFlower.position.set(0, 3.4845, 0);
  root.add(topFlower);
  const petal1 = new THREE.Mesh(octahedronGeo, petalMat);
  petal1.name = 'petal1';
  petal1.position.set(0.0995, 0.056, 0);
  petal1.rotation.set(0, 0, deg(32));
  petal1.scale.set(0.1368, 0.0348, 0.0746);
  topFlower.add(petal1);
  const petal2 = new THREE.Mesh(octahedronGeo, petalMat);
  petal2.name = 'petal2';
  petal2.position.set(0.0308, 0.056, -0.0946);
  petal2.rotation.set(0, deg(72), deg(32));
  petal2.scale.set(0.1368, 0.0348, 0.0746);
  topFlower.add(petal2);
  const petal3 = new THREE.Mesh(octahedronGeo, petalMat);
  petal3.name = 'petal3';
  petal3.position.set(-0.0805, 0.056, -0.0585);
  petal3.rotation.set(0, deg(144), deg(32));
  petal3.scale.set(0.1368, 0.0348, 0.0746);
  topFlower.add(petal3);
  const petal4 = new THREE.Mesh(octahedronGeo, petalMat);
  petal4.name = 'petal4';
  petal4.position.set(-0.0805, 0.056, 0.0585);
  petal4.rotation.set(0, deg(216), deg(32));
  petal4.scale.set(0.1368, 0.0348, 0.0746);
  topFlower.add(petal4);
  const petal5 = new THREE.Mesh(octahedronGeo, petalMat);
  petal5.name = 'petal5';
  petal5.position.set(0.0308, 0.056, 0.0946);
  petal5.rotation.set(0, deg(288), deg(32));
  petal5.scale.set(0.1368, 0.0348, 0.0746);
  topFlower.add(petal5);
  const center = new THREE.Mesh(new THREE.IcosahedronGeometry(0.056, 0), pollenMat);
  center.name = 'center';
  center.position.set(0, 0.0622, 0);
  topFlower.add(center);
  const arm1 = new THREE.Mesh(tube([[0, 1.3879, 0], [0.4342, 1.4289, 0], [0.687, 1.5726, 0], [0.701, 2.0475, 0], [0.701, 2.52, 0]], 0.2053, 14, 8), skinMat);
  arm1.name = 'arm1';
  root.add(arm1);
  const armTip1 = new THREE.Mesh(sphereGeo, skinMat);
  armTip1.name = 'armTip1';
  armTip1.position.set(0.701, 2.52, 0);
  root.add(armTip1);
  const armFlower1 = new THREE.Group();
  armFlower1.name = 'armFlower1';
  armFlower1.position.set(0.701, 2.7088, 0);
  root.add(armFlower1);
  const petal12 = new THREE.Mesh(octahedronGeo, petalMat);
  petal12.name = 'petal1';
  petal12.position.set(0.0657, 0.0369, 0);
  petal12.rotation.set(0, 0, deg(32));
  petal12.scale.set(0.0903, 0.023, 0.0493);
  armFlower1.add(petal12);
  const petal22 = new THREE.Mesh(octahedronGeo, petalMat);
  petal22.name = 'petal2';
  petal22.position.set(0.0203, 0.0369, -0.0625);
  petal22.rotation.set(0, deg(72), deg(32));
  petal22.scale.set(0.0903, 0.023, 0.0493);
  armFlower1.add(petal22);
  const petal32 = new THREE.Mesh(octahedronGeo, petalMat);
  petal32.name = 'petal3';
  petal32.position.set(-0.0531, 0.0369, -0.0386);
  petal32.rotation.set(0, deg(144), deg(32));
  petal32.scale.set(0.0903, 0.023, 0.0493);
  armFlower1.add(petal32);
  const petal42 = new THREE.Mesh(octahedronGeo, petalMat);
  petal42.name = 'petal4';
  petal42.position.set(-0.0531, 0.0369, 0.0386);
  petal42.rotation.set(0, deg(216), deg(32));
  petal42.scale.set(0.0903, 0.023, 0.0493);
  armFlower1.add(petal42);
  const petal52 = new THREE.Mesh(octahedronGeo, petalMat);
  petal52.name = 'petal5';
  petal52.position.set(0.0203, 0.0369, 0.0625);
  petal52.rotation.set(0, deg(288), deg(32));
  petal52.scale.set(0.0903, 0.023, 0.0493);
  armFlower1.add(petal52);
  const center2 = new THREE.Mesh(icosahedronGeo, pollenMat);
  center2.name = 'center';
  center2.position.set(0, 0.0411, 0);
  armFlower1.add(center2);
  const arm2 = new THREE.Mesh(tube([[0, 1.8429, 0], [-0.4342, 1.8839, 0], [-0.687, 2.0276, 0], [-0.701, 2.3485, 0], [-0.701, 2.695, 0]], 0.2053, 14, 8), skinMat);
  arm2.name = 'arm2';
  root.add(arm2);
  const armTip2 = new THREE.Mesh(sphereGeo, skinMat);
  armTip2.name = 'armTip2';
  armTip2.position.set(-0.701, 2.695, 0);
  root.add(armTip2);
  const armFlower2 = new THREE.Group();
  armFlower2.name = 'armFlower2';
  armFlower2.position.set(-0.701, 2.8838, 0);
  root.add(armFlower2);
  const petal13 = new THREE.Mesh(octahedronGeo, petalMat);
  petal13.name = 'petal1';
  petal13.position.set(0.0657, 0.0369, 0);
  petal13.rotation.set(0, 0, deg(32));
  petal13.scale.set(0.0903, 0.023, 0.0493);
  armFlower2.add(petal13);
  const petal23 = new THREE.Mesh(octahedronGeo, petalMat);
  petal23.name = 'petal2';
  petal23.position.set(0.0203, 0.0369, -0.0625);
  petal23.rotation.set(0, deg(72), deg(32));
  petal23.scale.set(0.0903, 0.023, 0.0493);
  armFlower2.add(petal23);
  const petal33 = new THREE.Mesh(octahedronGeo, petalMat);
  petal33.name = 'petal3';
  petal33.position.set(-0.0531, 0.0369, -0.0386);
  petal33.rotation.set(0, deg(144), deg(32));
  petal33.scale.set(0.0903, 0.023, 0.0493);
  armFlower2.add(petal33);
  const petal43 = new THREE.Mesh(octahedronGeo, petalMat);
  petal43.name = 'petal4';
  petal43.position.set(-0.0531, 0.0369, 0.0386);
  petal43.rotation.set(0, deg(216), deg(32));
  petal43.scale.set(0.0903, 0.023, 0.0493);
  armFlower2.add(petal43);
  const petal53 = new THREE.Mesh(octahedronGeo, petalMat);
  petal53.name = 'petal5';
  petal53.position.set(0.0203, 0.0369, 0.0625);
  petal53.rotation.set(0, deg(288), deg(32));
  petal53.scale.set(0.0903, 0.023, 0.0493);
  armFlower2.add(petal53);
  const center3 = new THREE.Mesh(icosahedronGeo, pollenMat);
  center3.name = 'center';
  center3.position.set(0, 0.0411, 0);
  armFlower2.add(center3);

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

export default createCactus;
```

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