# Grass Tuft

> An ultra-light low-poly clump of grass: flattened, tapering blades fanning out from a single root in two shades of green. Built for mass scattering and GPU instancing over terrain, with height, blade count, spread and color controls for meadows, lawns, forests and farm fields.

![Grass Tuft preview](https://codels.download/m/grass-tuft.png)

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

## Facts

- Category: Nature › Plants & Flowers
- Size (W × H × D): 0.36 × 0.36 × 0.42 m — units are meters, +Y up, front faces +Z
- Origin: center of the base (y = 0)
- Triangles: 72 · Meshes: 12 · Materials: 3
- License: CC0-1.0 (Codels built-in)
- Tags: grass, grass-tuft, tuft, low-poly, stylized, farm, forest, garden, grassland, swamp, farming-sim, rpg, background, decoration, vegetation, foliage-material, game-ready, instancing-friendly, mobile-friendly, parametric, color-green, size-small, poly-ultra-low
- Page: https://codels.download/models/grass-tuft

## Files

| Format | URL |
|---|---|
| three.js ES module (`createGrassTuft()`) | https://codels.download/m/grass-tuft.js |
| React Three Fiber component (`<GrassTuft />`) | https://codels.download/m/grass-tuft.jsx |
| Instancing module (`createGrassTuftInstances(count, place)`, one draw call per material) | https://codels.download/m/grass-tuft.instanced.js |
| Standalone HTML demo | https://codels.download/m/grass-tuft.html |
| glTF binary | https://codels.download/m/grass-tuft.glb |
| glTF binary, Meshopt-compressed (2–3× smaller; GLTFLoader needs `setMeshoptDecoder(MeshoptDecoder)`) | https://codels.download/m/grass-tuft.glb?compress=meshopt |
| glTF JSON | https://codels.download/m/grass-tuft.gltf |
| OBJ (vertex colors) + MTL | https://codels.download/m/grass-tuft.obj · https://codels.download/m/grass-tuft.mtl |
| STL | https://codels.download/m/grass-tuft.stl |
| CodelSpec JSON | https://codels.download/m/grass-tuft.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 | 0.15 – 0.9 | 0.4 | 0.4 |
| `blades` | int | 5 – 24 | 12 | 12 |
| `spread` | number | 0.03 – 0.4 | 0.12 | 0.12 |
| `color` | color | #rrggbb | #67a64a | #67a64a |

## Parts

- tuft (group)
  - blade1 (cone · grassDark)
  - blade2 (cone · grass)
  - blade3 (cone · grassLight)
  - blade4 (cone · grassDark)
  - blade5 (cone · grass)
  - blade6 (cone · grassLight)
  - blade7 (cone · grassDark)
  - blade8 (cone · grass)
  - blade9 (cone · grassLight)
  - blade10 (cone · grassDark)
  - blade11 (cone · grass)
  - blade12 (cone · grassLight)

## How to use

Save the code below as `grass-tuft.js`, then:

```js
import { createGrassTuft } from './grass-tuft.js';
const model = createGrassTuft();
scene.add(model);
```

## Code

```js
/**
 * Grass Tuft — Codels 3D model library (https://codels.download/models/grass-tuft)
 * License: CC0-1.0 · Units: meters · Y-up · Front: +Z · Origin: center of the base (y = 0)
 * Triangles: 72 · Size (W × H × D): 0.36 × 0.36 × 0.42 m
 *
 * Usage:
 *   import { createGrassTuft } from './grass-tuft.js';
 *   
 *   const model = createGrassTuft();
 *   model.position.set(0, 0, 0);
 *   scene.add(model);
 */
import * as THREE from 'three';

const deg = THREE.MathUtils.degToRad;

export function createGrassTuft() {
  const root = new THREE.Group();
  root.name = 'GrassTuft';

  // Materials
  const grassMat = new THREE.MeshStandardMaterial({ name: 'grass', color: 0x67a64a, roughness: 0.9, flatShading: true });
  const grassDarkMat = new THREE.MeshStandardMaterial({ name: 'grassDark', color: 0x5c883d, roughness: 0.9, flatShading: true });
  const grassLightMat = new THREE.MeshStandardMaterial({ name: 'grassLight', color: 0x6baf5c, roughness: 0.9, flatShading: true });

  // Parts
  const tuft = new THREE.Group();
  tuft.name = 'tuft';
  tuft.position.set(0, 0.0059, 0);
  root.add(tuft);
  const blade1 = new THREE.Mesh(new THREE.ConeGeometry(0.033, 0.3445, 3), grassDarkMat);
  blade1.name = 'blade1';
  blade1.position.set(0.0519, 0.1679, -0.0111);
  blade1.rotation.set(deg(132.4025), deg(72.4357), deg(-133.767));
  blade1.scale.set(1, 1, 0.35);
  tuft.add(blade1);
  const blade2 = new THREE.Mesh(new THREE.ConeGeometry(0.0261, 0.2883, 3), grassMat);
  blade2.name = 'blade2';
  blade2.position.set(-0.0679, 0.1378, 0.047);
  blade2.rotation.set(deg(28.2424), deg(-51.8866), deg(22.9101));
  blade2.scale.set(1, 1, 0.35);
  tuft.add(blade2);
  const blade3 = new THREE.Mesh(new THREE.ConeGeometry(0.0282, 0.368, 3), grassLightMat);
  blade3.name = 'blade3';
  blade3.position.set(-0.0073, 0.176, -0.1055);
  blade3.rotation.set(deg(162.9761), deg(-3.7891), deg(178.8408));
  blade3.scale.set(1, 1, 0.35);
  tuft.add(blade3);
  const blade4 = new THREE.Mesh(new THREE.ConeGeometry(0.0269, 0.2928, 3), grassDarkMat);
  blade4.name = 'blade4';
  blade4.position.set(0.0589, 0.1401, 0.0867);
  blade4.rotation.set(deg(20.1544), deg(32.5309), deg(-11.1649));
  blade4.scale.set(1, 1, 0.35);
  tuft.add(blade4);
  const blade5 = new THREE.Mesh(new THREE.ConeGeometry(0.029, 0.3047, 3), grassMat);
  blade5.name = 'blade5';
  blade5.position.set(-0.095, 0.1482, 0.0103);
  blade5.rotation.set(deg(65.6164), deg(-75.3138), deg(64.893));
  blade5.scale.set(1, 1, 0.35);
  tuft.add(blade5);
  const blade6 = new THREE.Mesh(new THREE.ConeGeometry(0.0265, 0.2935, 3), grassLightMat);
  blade6.name = 'blade6';
  blade6.position.set(0.1067, 0.1313, -0.0646);
  blade6.rotation.set(deg(136.0447), deg(49.918), deg(-143.5831));
  blade6.scale.set(1, 1, 0.35);
  tuft.add(blade6);
  const blade7 = new THREE.Mesh(new THREE.ConeGeometry(0.027, 0.2937, 3), grassDarkMat);
  blade7.name = 'blade7';
  blade7.position.set(-0.0587, 0.1275, 0.1174);
  blade7.rotation.set(deg(32.534), deg(-22.8403), deg(13.9073));
  blade7.scale.set(1, 1, 0.35);
  tuft.add(blade7);
  const blade8 = new THREE.Mesh(new THREE.ConeGeometry(0.0309, 0.2492, 3), grassMat);
  blade8.name = 'blade8';
  blade8.position.set(-0.0681, 0.1139, -0.1217);
  blade8.rotation.set(deg(153.0703), deg(-26.513), deg(167.2235));
  blade8.scale.set(1, 1, 0.35);
  tuft.add(blade8);
  const blade9 = new THREE.Mesh(new THREE.ConeGeometry(0.0323, 0.2574, 3), grassLightMat);
  blade9.name = 'blade9';
  blade9.position.set(0.1245, 0.1097, 0.0616);
  blade9.rotation.set(deg(54.1479), deg(49.7995), deg(-46.5871));
  blade9.scale.set(1, 1, 0.35);
  tuft.add(blade9);
  const blade10 = new THREE.Mesh(new THREE.ConeGeometry(0.0279, 0.238, 3), grassDarkMat);
  blade10.name = 'blade10';
  blade10.position.set(-0.1285, 0.1045, 0.0795);
  blade10.rotation.set(deg(45.9759), deg(-48.3011), deg(37.6871));
  blade10.scale.set(1, 1, 0.35);
  tuft.add(blade10);
  const blade11 = new THREE.Mesh(new THREE.ConeGeometry(0.0248, 0.233, 3), grassMat);
  blade11.name = 'blade11';
  blade11.position.set(0.0719, 0.0902, -0.1477);
  blade11.rotation.set(deg(137.768), deg(19.8255), deg(-162.8878));
  blade11.scale.set(1, 1, 0.35);
  tuft.add(blade11);
  const blade12 = new THREE.Mesh(new THREE.ConeGeometry(0.0273, 0.1919, 3), grassLightMat);
  blade12.name = 'blade12';
  blade12.position.set(0.0076, 0.0758, 0.1428);
  blade12.rotation.set(deg(37.8795), deg(2.4185), deg(-1.8801));
  blade12.scale.set(1, 1, 0.35);
  tuft.add(blade12);

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

export default createGrassTuft;
```

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