# Health Pack

> A chunky first-aid kit case with rounded beveled edges, a bold red cross on the front and lid, a carry handle, side latches and a lid seam. About 0.42 m wide, standing upright facing +Z. Recolor the case and cross for health pickups, medkits and supply drops in shooters, survival games and platformers.

![Health Pack preview](https://codels.download/m/health-pack.png)

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

## Facts

- Category: Items & Weapons › Consumables
- Size (W × H × D): 0.43 × 0.37 × 0.18 m — units are meters, +Y up, front faces +Z
- Origin: center of the base (y = 0)
- Triangles: 612 · Meshes: 14 · Materials: 4
- License: CC0-1.0 (Codels built-in)
- Tags: medkit, cartoon, low-poly, stylized, military, modern, sci-fi, fps, shooter, survival, consumable, container, pickup, prop, plastic, game-ready, mobile-friendly, parametric, color-white, size-small, poly-low
- Page: https://codels.download/models/health-pack

## Files

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

## Parameters

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

| Param | Type | Range / options | Default | Current |
|---|---|---|---|---|
| `size` | number | 0.5 – 2 | 1 | 1 |
| `body` | color | #rrggbb | #f1f0ea | #f1f0ea |
| `cross` | color | #rrggbb | #e0353a | #e0353a |

## Parts

- case (extrude · body)
- seamFront (box · trim)
- seamRight (box · trim)
- seamBack (box · trim)
- seamLeft (box · trim)
- crossFront (extrude · cross)
- crossTop (extrude · cross)
- latchR (box · trim)
- latchTabR (box · dark)
- latchL (box · trim)
- latchTabL (box · dark)
- handle (tube · dark)
- handleMountR (box · trim)
- handleMountL (box · trim)

## How to use

Save the code below as `health-pack.js`, then:

```js
import { createHealthPack } from './health-pack.js';
const model = createHealthPack();
scene.add(model);
```

## Code

```js
/**
 * Health Pack — Codels 3D model library (https://codels.download/models/health-pack)
 * License: CC0-1.0 · Units: meters · Y-up · Front: +Z · Origin: center of the base (y = 0)
 * Triangles: 612 · Size (W × H × D): 0.43 × 0.37 × 0.18 m
 *
 * Usage:
 *   import { createHealthPack } from './health-pack.js';
 *   
 *   const model = createHealthPack();
 *   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);
}

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

  // Materials
  const bodyMat = new THREE.MeshStandardMaterial({ name: 'body', color: 0xf1f0ea, roughness: 0.5, flatShading: true });
  const crossMat = new THREE.MeshStandardMaterial({ name: 'cross', color: 0xe0353a, roughness: 0.5, flatShading: true });
  const trimMat = new THREE.MeshStandardMaterial({ name: 'trim', color: 0x9aa1aa, roughness: 0.4, metalness: 0.6, flatShading: true });
  const darkMat = new THREE.MeshStandardMaterial({ name: 'dark', color: 0x3a3f47, roughness: 0.7, flatShading: true });

  // Shared geometries
  const boxGeo = new THREE.BoxGeometry(0.392, 0.008, 0.006);
  const boxGeo2 = new THREE.BoxGeometry(0.006, 0.008, 0.1332);
  const boxGeo3 = new THREE.BoxGeometry(0.045, 0.04, 0.012);
  const boxGeo4 = new THREE.BoxGeometry(0.025, 0.012, 0.008);
  const boxGeo5 = new THREE.BoxGeometry(0.035, 0.014, 0.04);

  // Parts
  const case2 = new THREE.Mesh(extrude([[0.165, -0.135], [0.1825, -0.1303], [0.1953, -0.1175], [0.2, -0.1], [0.2, 0.1], [0.1953, 0.1175], [0.1825, 0.1303], [0.165, 0.135], [-0.165, 0.135], [-0.1825, 0.1303], [-0.1953, 0.1175], [-0.2, 0.1], [-0.2, -0.1], [-0.1953, -0.1175], [-0.1825, -0.1303], [-0.165, -0.135]], 0.12, [], [0.022, 0.014, 2]), bodyMat);
  case2.name = 'case';
  case2.position.set(0, 0.149, 0);
  root.add(case2);
  const seamFront = new THREE.Mesh(boxGeo, trimMat);
  seamFront.name = 'seamFront';
  seamFront.position.set(0, 0.2084, 0.081);
  root.add(seamFront);
  const seamRight = new THREE.Mesh(boxGeo2, trimMat);
  seamRight.name = 'seamRight';
  seamRight.position.set(0.213, 0.2084, 0);
  root.add(seamRight);
  const seamBack = new THREE.Mesh(boxGeo, trimMat);
  seamBack.name = 'seamBack';
  seamBack.position.set(0, 0.2084, -0.081);
  root.add(seamBack);
  const seamLeft = new THREE.Mesh(boxGeo2, trimMat);
  seamLeft.name = 'seamLeft';
  seamLeft.position.set(-0.213, 0.2084, 0);
  root.add(seamLeft);
  const crossFront = new THREE.Mesh(extrude([[-0.025, -0.075], [0.025, -0.075], [0.025, -0.025], [0.075, -0.025], [0.075, 0.025], [0.025, 0.025], [0.025, 0.075], [-0.025, 0.075], [-0.025, 0.025], [-0.075, 0.025], [-0.075, -0.025], [-0.025, -0.025]], 0.006), crossMat);
  crossFront.name = 'crossFront';
  crossFront.position.set(0, 0.1274, 0.083);
  root.add(crossFront);
  const crossTop = new THREE.Mesh(extrude([[-0.015, -0.0425], [0.015, -0.0425], [0.015, -0.015], [0.0425, -0.015], [0.0425, 0.015], [0.015, 0.015], [0.015, 0.0425], [-0.015, 0.0425], [-0.015, 0.015], [-0.0425, 0.015], [-0.0425, -0.015], [-0.015, -0.015]], 0.006), crossMat);
  crossTop.name = 'crossTop';
  crossTop.position.set(-0.12, 0.299, 0);
  crossTop.rotation.set(deg(-90), 0, 0);
  root.add(crossTop);
  const latchR = new THREE.Mesh(boxGeo3, trimMat);
  latchR.name = 'latchR';
  latchR.position.set(0.132, 0.2084, 0.086);
  root.add(latchR);
  const latchTabR = new THREE.Mesh(boxGeo4, darkMat);
  latchTabR.name = 'latchTabR';
  latchTabR.position.set(0.132, 0.1964, 0.093);
  root.add(latchTabR);
  const latchL = new THREE.Mesh(boxGeo3, trimMat);
  latchL.name = 'latchL';
  latchL.position.set(-0.132, 0.2084, 0.086);
  root.add(latchL);
  const latchTabL = new THREE.Mesh(boxGeo4, darkMat);
  latchTabL.name = 'latchTabL';
  latchTabL.position.set(-0.132, 0.1964, 0.093);
  root.add(latchTabL);
  const handle = new THREE.Mesh(tube([[-0.075, 0.294, 0], [-0.07, 0.333, 0], [-0.04, 0.353, 0], [0.04, 0.353, 0], [0.07, 0.333, 0], [0.075, 0.294, 0]], 0.012, 18, 6), darkMat);
  handle.name = 'handle';
  root.add(handle);
  const handleMountR = new THREE.Mesh(boxGeo5, trimMat);
  handleMountR.name = 'handleMountR';
  handleMountR.position.set(0.075, 0.301, 0);
  root.add(handleMountR);
  const handleMountL = new THREE.Mesh(boxGeo5, trimMat);
  handleMountL.name = 'handleMountL';
  handleMountL.position.set(-0.075, 0.301, 0);
  root.add(handleMountL);

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

export default createHealthPack;
```

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