# Magic Staff

> A gnarled wizard staff (about 1.5 m) whose wooden shaft splits into three twisted roots that cradle a glowing emissive orb or a floating crystal. Gold collar, metal ferrule and an adjustable glow color make it a perfect mage weapon, magic pickup or wizard tower prop for fantasy RPGs.

![Magic Staff preview](https://codels.download/m/magic-staff.png)

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

## Facts

- Category: Items & Weapons › Weapons
- Size (W × H × D): 0.18 × 1.48 × 0.19 m — units are meters, +Y up, front faces +Z
- Origin: center of the base (y = 0)
- Triangles: 1,388 · Meshes: 16 · Materials: 4
- License: CC0-1.0 (Codels built-in)
- Tags: magic-staff, staff, wizard, low-poly, stylized, fantasy, adventure, roguelike, rpg, hero-asset, weapon, energy, wood, emissive, game-ready, mobile-friendly, parametric, color-brown, size-medium, poly-low
- Page: https://codels.download/models/magic-staff

## Files

| Format | URL |
|---|---|
| three.js ES module (`createMagicStaff()`) | https://codels.download/m/magic-staff.js |
| React Three Fiber component (`<MagicStaff />`) | https://codels.download/m/magic-staff.jsx |
| Instancing module (`createMagicStaffInstances(count, place)`, one draw call per material) | https://codels.download/m/magic-staff.instanced.js |
| Standalone HTML demo | https://codels.download/m/magic-staff.html |
| glTF binary | https://codels.download/m/magic-staff.glb |
| glTF binary, Meshopt-compressed (2–3× smaller; GLTFLoader needs `setMeshoptDecoder(MeshoptDecoder)`) | https://codels.download/m/magic-staff.glb?compress=meshopt |
| glTF JSON | https://codels.download/m/magic-staff.gltf |
| OBJ (vertex colors) + MTL | https://codels.download/m/magic-staff.obj · https://codels.download/m/magic-staff.mtl |
| STL | https://codels.download/m/magic-staff.stl |
| CodelSpec JSON | https://codels.download/m/magic-staff.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.1 – 1.9 | 1.5 | 1.5 |
| `glow` | color | #rrggbb | #53d6ff | #53d6ff |
| `topper` | select | orb \| crystal | orb | orb |
| `wood` | color | #rrggbb | #6e4a2e | #6e4a2e |

## Parts

- ferrule (cylinder · gold)
- shaft (tube · wood)
- knot1 (sphere · wood)
- knot2 (sphere · wood)
- root1 (tube · wood)
- rootTip1 (sphere · wood)
- root2 (tube · wood)
- rootTip2 (sphere · wood)
- root3 (tube · wood)
- rootTip3 (sphere · wood)
- collar (cylinder · gold)
- collarRing (cylinder · gold)
- orb (sphere · glow)
- collarGem1 (octahedron · crystal)
- collarGem2 (octahedron · crystal)
- collarGem3 (octahedron · crystal)

## How to use

Save the code below as `magic-staff.js`, then:

```js
import { createMagicStaff } from './magic-staff.js';
const model = createMagicStaff();
scene.add(model);
```

## Code

```js
/**
 * Magic Staff — Codels 3D model library (https://codels.download/models/magic-staff)
 * License: CC0-1.0 · Units: meters · Y-up · Front: +Z · Origin: center of the base (y = 0)
 * Triangles: 1,388 · Size (W × H × D): 0.18 × 1.48 × 0.19 m
 *
 * Usage:
 *   import { createMagicStaff } from './magic-staff.js';
 *   
 *   const model = createMagicStaff();
 *   model.position.set(0, 0, 0);
 *   scene.add(model);
 */
import * as THREE from 'three';

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

  // Materials
  const woodMat = new THREE.MeshStandardMaterial({ name: 'wood', color: 0x6e4a2e, roughness: 0.85, flatShading: true });
  const goldMat = new THREE.MeshStandardMaterial({ name: 'gold', color: 0xd4a84a, roughness: 0.35, metalness: 0.75, flatShading: true });
  const glowMat = new THREE.MeshStandardMaterial({ name: 'glow', color: 0x53d6ff, roughness: 0.25, emissive: 0x53d6ff, emissiveIntensity: 0.9 });
  const crystalMat = new THREE.MeshStandardMaterial({ name: 'crystal', color: 0x53d6ff, roughness: 0.2, emissive: 0x53d6ff, emissiveIntensity: 0.6, flatShading: true });

  // Shared geometries
  const sphereGeo = new THREE.SphereGeometry(0.016, 6, 4);
  const octahedronGeo = new THREE.OctahedronGeometry(0.012, 0);

  // Parts
  const ferrule = new THREE.Mesh(new THREE.CylinderGeometry(0.027, 0.02, 0.07, 7), goldMat);
  ferrule.name = 'ferrule';
  ferrule.position.set(0, 0.035, 0);
  root.add(ferrule);
  const shaft = new THREE.Mesh(tube([[0.0001, 0.03, 0.0007], [0.0025, 0.1444, 0.0036], [0.007, 0.2589, 0.0059], [0.0112, 0.3733, 0.0066], [0.0104, 0.4878, 0.0042], [0.0063, 0.6022, 0.0007], [0.0007, 0.7167, -0.0028], [-0.005, 0.8311, -0.0059], [-0.0096, 0.9456, -0.0084], [-0.0119, 1.06, -0.0097]], 0.025, 20, 6), woodMat);
  shaft.name = 'shaft';
  root.add(shaft);
  const knot1 = new THREE.Mesh(new THREE.SphereGeometry(0.03, 6, 4), woodMat);
  knot1.name = 'knot1';
  knot1.position.set(0.0196, 0.57, 0.0017);
  knot1.scale.set(1, 1.4, 1);
  root.add(knot1);
  const knot2 = new THREE.Mesh(new THREE.SphereGeometry(0.026, 6, 4), woodMat);
  knot2.name = 'knot2';
  knot2.position.set(-0.0191, 0.93, -0.0021);
  knot2.scale.set(1, 1.3, 1);
  root.add(knot2);
  const root1 = new THREE.Mesh(tube([[0.003, 1, -0.0092], [0.0066, 1.046, -0.0019], [0.006, 1.092, 0.0078], [0.0001, 1.138, 0.0176], [-0.011, 1.184, 0.0334], [-0.0325, 1.23, 0.0427], [-0.0602, 1.276, 0.0401], [-0.0878, 1.322, 0.0227], [-0.0995, 1.368, -0.0092], [-0.0676, 1.414, -0.0326], [-0.035, 1.46, -0.0332]], 0.014, 22, 5), woodMat);
  root1.name = 'root1';
  root.add(root1);
  const rootTip1 = new THREE.Mesh(sphereGeo, woodMat);
  rootTip1.name = 'rootTip1';
  rootTip1.position.set(-0.035, 1.46, -0.0332);
  root.add(rootTip1);
  const root2 = new THREE.Mesh(tube([[-0.018, 1, 0.003], [-0.0261, 1.046, 0.0024], [-0.0342, 1.092, -0.003], [-0.0397, 1.138, -0.0129], [-0.0478, 1.184, -0.0304], [-0.0451, 1.23, -0.0537], [-0.029, 1.276, -0.0764], [-0.0001, 1.322, -0.0916], [0.0333, 1.368, -0.0858], [0.0376, 1.414, -0.0465], [0.0218, 1.46, -0.018]], 0.014, 22, 5), woodMat);
  root2.name = 'root2';
  root.add(root2);
  const rootTip2 = new THREE.Mesh(sphereGeo, woodMat);
  rootTip2.name = 'rootTip2';
  rootTip2.position.set(0.0218, 1.46, -0.018);
  root.add(rootTip2);
  const root3 = new THREE.Mesh(tube([[-0.018, 1, -0.0213], [-0.0135, 1.046, -0.028], [-0.0048, 1.092, -0.0323], [0.0067, 1.138, -0.0322], [0.0258, 1.184, -0.0304], [0.0446, 1.23, -0.0165], [0.0563, 1.276, 0.0089], [0.055, 1.322, 0.0415], [0.0333, 1.368, 0.0675], [-0.003, 1.414, 0.0516], [-0.0198, 1.46, 0.0237]], 0.014, 22, 5), woodMat);
  root3.name = 'root3';
  root.add(root3);
  const rootTip3 = new THREE.Mesh(sphereGeo, woodMat);
  rootTip3.name = 'rootTip3';
  rootTip3.position.set(-0.0198, 1.46, 0.0237);
  root.add(rootTip3);
  const collar = new THREE.Mesh(new THREE.CylinderGeometry(0.034, 0.034, 0.05, 8), goldMat);
  collar.name = 'collar';
  collar.position.set(-0.011, 1.01, -0.0092);
  root.add(collar);
  const collarRing = new THREE.Mesh(new THREE.CylinderGeometry(0.038, 0.038, 0.012, 8), goldMat);
  collarRing.name = 'collarRing';
  collarRing.position.set(-0.011, 1.05, -0.0092);
  root.add(collarRing);
  const orb = new THREE.Mesh(new THREE.SphereGeometry(0.075, 12, 9), glowMat);
  orb.name = 'orb';
  orb.position.set(-0.011, 1.34, -0.0092);
  root.add(orb);
  const collarGem1 = new THREE.Mesh(octahedronGeo, crystalMat);
  collarGem1.name = 'collarGem1';
  collarGem1.position.set(0.0185, 1.01, 0.0078);
  root.add(collarGem1);
  const collarGem2 = new THREE.Mesh(octahedronGeo, crystalMat);
  collarGem2.name = 'collarGem2';
  collarGem2.position.set(-0.0404, 1.01, 0.0078);
  root.add(collarGem2);
  const collarGem3 = new THREE.Mesh(octahedronGeo, crystalMat);
  collarGem3.name = 'collarGem3';
  collarGem3.position.set(-0.011, 1.01, -0.0432);
  root.add(collarGem3);

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

export default createMagicStaff;
```

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