# Skeleton Warrior

> A blocky, chibi-proportioned skeleton warrior with a big grinning skull, glowing eye sockets, stacked ribs and knobby joints, wielding a sword or axe. Optional rusty helmet and round wooden shield. Head, arms (with elbow pivots) and legs are separate groups, ready for shamble and swing animations in dungeon crawlers, RPGs and Halloween levels.

![Skeleton Warrior preview](https://codels.download/m/skeleton-warrior.png)

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

## Facts

- Category: Characters › Creatures & Monsters
- Size (W × H × D): 0.86 × 1.50 × 0.64 m — units are meters, +Y up, front faces +Z
- Origin: center of the base (y = 0)
- Triangles: 664 · Meshes: 49 · Materials: 8
- License: CC0-1.0 (Codels built-in)
- Tags: shield, skeleton, sword, warrior, blocky, cartoon, low-poly, fantasy, halloween, horror, medieval, undead, dungeon, roguelike, rpg, enemy, game-ready, mobile-friendly, parametric, color-orange, size-medium, poly-low
- Page: https://codels.download/models/skeleton-warrior

## Files

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

## Parameters

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

| Param | Type | Range / options | Default | Current |
|---|---|---|---|---|
| `bone` | color | #rrggbb | #ebe3cf | #ebe3cf |
| `eyes` | color | #rrggbb | #ff5a3c | #ff5a3c |
| `weapon` | select | sword \| axe | sword | sword |
| `shield` | boolean | true \| false | true | true |
| `helmet` | boolean | true \| false | false | false |

## Parts

- legL (group)
  - thighL (box · bone)
  - kneeL (box · joint)
  - shinL (box · bone)
  - footL (box · bone)
- legR (group)
  - thighR (box · bone)
  - kneeR (box · joint)
  - shinR (box · bone)
  - footR (box · bone)
- body (group)
  - pelvis (box · bone)
  - spine (box · joint)
  - rib1 (box · bone)
  - rib2 (box · bone)
  - rib3 (box · bone)
  - rib4 (box · bone)
  - sternum (box · bone)
  - collarbone (box · bone)
  - belt (box · leather)
  - loincloth (box · leather)
- armL (group)
  - shoulderL (box · joint)
  - upperArmL (box · bone)
  - forearmL (group)
    - elbowL (box · joint)
    - lowerArmL (box · bone)
    - handL (box · bone)
    - shield (group)
      - shieldBoard (cylinder · wood)
      - shieldRim (cylinder · iron)
      - shieldBoss (cylinder · iron)
      - shieldBand (box · iron)
- armR (group)
  - shoulderR (box · joint)
  - upperArmR (box · bone)
  - forearmR (group)
    - elbowR (box · joint)
    - lowerArmR (box · bone)
    - handR (box · bone)

## How to use

Save the code below as `skeleton-warrior.js`, then:

```js
import { createSkeletonWarrior } from './skeleton-warrior.js';
const model = createSkeletonWarrior();
scene.add(model);
```

## Code

```js
/**
 * Skeleton Warrior — Codels 3D model library (https://codels.download/models/skeleton-warrior)
 * License: CC0-1.0 · Units: meters · Y-up · Front: +Z · Origin: center of the base (y = 0)
 * Triangles: 664 · Size (W × H × D): 0.86 × 1.50 × 0.64 m
 *
 * Usage:
 *   import { createSkeletonWarrior } from './skeleton-warrior.js';
 *   
 *   const model = createSkeletonWarrior();
 *   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);
}

export function createSkeletonWarrior() {
  const root = new THREE.Group();
  root.name = 'SkeletonWarrior';

  // Materials
  const boneMat = new THREE.MeshStandardMaterial({ name: 'bone', color: 0xebe3cf, roughness: 0.75, flatShading: true });
  const jointMat = new THREE.MeshStandardMaterial({ name: 'joint', color: 0xcac3b2, roughness: 0.8, flatShading: true });
  const socketMat = new THREE.MeshStandardMaterial({ name: 'socket', color: 0x2b2329, roughness: 0.9, flatShading: true });
  const glowMat = new THREE.MeshStandardMaterial({ name: 'glow', color: 0xff5a3c, roughness: 0.4, emissive: 0xff5a3c, emissiveIntensity: 1.6, flatShading: true });
  const steelMat = new THREE.MeshStandardMaterial({ name: 'steel', color: 0xb4bcc6, roughness: 0.3, metalness: 0.7, flatShading: true });
  const ironMat = new THREE.MeshStandardMaterial({ name: 'iron', color: 0x585c66, roughness: 0.55, metalness: 0.5, flatShading: true });
  const woodMat = new THREE.MeshStandardMaterial({ name: 'wood', color: 0x83582f, roughness: 0.85, flatShading: true });
  const leatherMat = new THREE.MeshStandardMaterial({ name: 'leather', color: 0x5b3a28, roughness: 0.8, flatShading: true });

  // Shared geometries
  const boxGeo = new THREE.BoxGeometry(0.075, 0.24, 0.075);
  const boxGeo2 = new THREE.BoxGeometry(0.11, 0.075, 0.11);
  const boxGeo3 = new THREE.BoxGeometry(0.07, 0.22, 0.07);
  const boxGeo4 = new THREE.BoxGeometry(0.13, 0.065, 0.2);
  const boxGeo5 = new THREE.BoxGeometry(0.11, 0.1, 0.11);
  const boxGeo6 = new THREE.BoxGeometry(0.065, 0.2, 0.065);
  const boxGeo7 = new THREE.BoxGeometry(0.09, 0.07, 0.09);
  const boxGeo8 = new THREE.BoxGeometry(0.06, 0.18, 0.06);
  const boxGeo9 = new THREE.BoxGeometry(0.1, 0.1, 0.1);
  const boxGeo10 = new THREE.BoxGeometry(0.008, 0.05, 0.008);
  const boxGeo11 = new THREE.BoxGeometry(0.1, 0.1, 0.02);
  const boxGeo12 = new THREE.BoxGeometry(0.04, 0.04, 0.01);

  // Parts
  const legL = new THREE.Group();
  legL.name = 'legL';
  legL.position.set(0.1, 0.6, 0);
  root.add(legL);
  const thighL = new THREE.Mesh(boxGeo, boneMat);
  thighL.name = 'thighL';
  thighL.position.set(0, -0.14, 0);
  legL.add(thighL);
  const kneeL = new THREE.Mesh(boxGeo2, jointMat);
  kneeL.name = 'kneeL';
  kneeL.position.set(0, -0.29, 0);
  legL.add(kneeL);
  const shinL = new THREE.Mesh(boxGeo3, boneMat);
  shinL.name = 'shinL';
  shinL.position.set(0, -0.43, 0);
  legL.add(shinL);
  const footL = new THREE.Mesh(boxGeo4, boneMat);
  footL.name = 'footL';
  footL.position.set(0, -0.5675, 0.035);
  legL.add(footL);
  const legR = new THREE.Group();
  legR.name = 'legR';
  legR.position.set(-0.1, 0.6, 0);
  root.add(legR);
  const thighR = new THREE.Mesh(boxGeo, boneMat);
  thighR.name = 'thighR';
  thighR.position.set(0, -0.14, 0);
  legR.add(thighR);
  const kneeR = new THREE.Mesh(boxGeo2, jointMat);
  kneeR.name = 'kneeR';
  kneeR.position.set(0, -0.29, 0);
  legR.add(kneeR);
  const shinR = new THREE.Mesh(boxGeo3, boneMat);
  shinR.name = 'shinR';
  shinR.position.set(0, -0.43, 0);
  legR.add(shinR);
  const footR = new THREE.Mesh(boxGeo4, boneMat);
  footR.name = 'footR';
  footR.position.set(0, -0.5675, 0.035);
  legR.add(footR);
  const body = new THREE.Group();
  body.name = 'body';
  root.add(body);
  const pelvis = new THREE.Mesh(new THREE.BoxGeometry(0.3, 0.1, 0.15), boneMat);
  pelvis.name = 'pelvis';
  pelvis.position.set(0, 0.62, 0);
  body.add(pelvis);
  const spine = new THREE.Mesh(new THREE.BoxGeometry(0.065, 0.42, 0.065), jointMat);
  spine.name = 'spine';
  spine.position.set(0, 0.85, -0.04);
  body.add(spine);
  const rib1 = new THREE.Mesh(new THREE.BoxGeometry(0.304, 0.04, 0.2), boneMat);
  rib1.name = 'rib1';
  rib1.position.set(0, 0.8, -0.005);
  body.add(rib1);
  const rib2 = new THREE.Mesh(new THREE.BoxGeometry(0.334, 0.04, 0.2), boneMat);
  rib2.name = 'rib2';
  rib2.position.set(0, 0.87, -0.005);
  body.add(rib2);
  const rib3 = new THREE.Mesh(new THREE.BoxGeometry(0.316, 0.04, 0.2), boneMat);
  rib3.name = 'rib3';
  rib3.position.set(0, 0.94, -0.005);
  body.add(rib3);
  const rib4 = new THREE.Mesh(new THREE.BoxGeometry(0.286, 0.04, 0.2), boneMat);
  rib4.name = 'rib4';
  rib4.position.set(0, 1.01, -0.005);
  body.add(rib4);
  const sternum = new THREE.Mesh(new THREE.BoxGeometry(0.05, 0.25, 0.03), boneMat);
  sternum.name = 'sternum';
  sternum.position.set(0, 0.91, 0.1);
  body.add(sternum);
  const collarbone = new THREE.Mesh(new THREE.BoxGeometry(0.44, 0.06, 0.12), boneMat);
  collarbone.name = 'collarbone';
  collarbone.position.set(0, 1.1, -0.01);
  body.add(collarbone);
  const belt = new THREE.Mesh(new THREE.BoxGeometry(0.32, 0.05, 0.17), leatherMat);
  belt.name = 'belt';
  belt.position.set(0, 0.675, 0);
  body.add(belt);
  const loincloth = new THREE.Mesh(new THREE.BoxGeometry(0.14, 0.16, 0.02), leatherMat);
  loincloth.name = 'loincloth';
  loincloth.position.set(0, 0.56, 0.085);
  body.add(loincloth);
  const armL = new THREE.Group();
  armL.name = 'armL';
  armL.position.set(0.25, 1.1, -0.01);
  armL.rotation.set(deg(6), 0, deg(8));
  root.add(armL);
  const shoulderL = new THREE.Mesh(boxGeo5, jointMat);
  shoulderL.name = 'shoulderL';
  armL.add(shoulderL);
  const upperArmL = new THREE.Mesh(boxGeo6, boneMat);
  upperArmL.name = 'upperArmL';
  upperArmL.position.set(0, -0.14, 0);
  armL.add(upperArmL);
  const forearmL = new THREE.Group();
  forearmL.name = 'forearmL';
  forearmL.position.set(0, -0.26, 0);
  forearmL.rotation.set(deg(-35), 0, 0);
  armL.add(forearmL);
  const elbowL = new THREE.Mesh(boxGeo7, jointMat);
  elbowL.name = 'elbowL';
  forearmL.add(elbowL);
  const lowerArmL = new THREE.Mesh(boxGeo8, boneMat);
  lowerArmL.name = 'lowerArmL';
  lowerArmL.position.set(0, -0.12, 0);
  forearmL.add(lowerArmL);
  const handL = new THREE.Mesh(boxGeo9, boneMat);
  handL.name = 'handL';
  handL.position.set(0, -0.25, 0);
  forearmL.add(handL);
  const shield = new THREE.Group();
  shield.name = 'shield';
  shield.position.set(0.08, -0.15, 0.02);
  forearmL.add(shield);
  const shieldBoard = new THREE.Mesh(new THREE.CylinderGeometry(0.21, 0.21, 0.045, 10), woodMat);
  shieldBoard.name = 'shieldBoard';
  shieldBoard.rotation.set(0, 0, deg(90));
  shield.add(shieldBoard);
  const shieldRim = new THREE.Mesh(new THREE.CylinderGeometry(0.215, 0.215, 0.03, 10, 1, false), ironMat);
  shieldRim.name = 'shieldRim';
  shieldRim.rotation.set(0, 0, deg(90));
  shieldRim.scale.set(1, 1.2, 1);
  shield.add(shieldRim);
  const shieldBoss = new THREE.Mesh(new THREE.CylinderGeometry(0.06, 0.07, 0.04, 8), ironMat);
  shieldBoss.name = 'shieldBoss';
  shieldBoss.position.set(0.04, 0, 0);
  shieldBoss.rotation.set(0, 0, deg(90));
  shield.add(shieldBoss);
  const shieldBand = new THREE.Mesh(new THREE.BoxGeometry(0.05, 0.4, 0.04), ironMat);
  shieldBand.name = 'shieldBand';
  shieldBand.position.set(0.025, 0, 0);
  shield.add(shieldBand);
  const armR = new THREE.Group();
  armR.name = 'armR';
  armR.position.set(-0.25, 1.1, -0.01);
  armR.rotation.set(deg(10), 0, deg(-8));
  root.add(armR);
  const shoulderR = new THREE.Mesh(boxGeo5, jointMat);
  shoulderR.name = 'shoulderR';
  armR.add(shoulderR);
  const upperArmR = new THREE.Mesh(boxGeo6, boneMat);
  upperArmR.name = 'upperArmR';
  upperArmR.position.set(0, -0.14, 0);
  armR.add(upperArmR);
  const forearmR = new THREE.Group();
  forearmR.name = 'forearmR';
  forearmR.position.set(0, -0.26, 0);
  forearmR.rotation.set(deg(-80), 0, 0);
  armR.add(forearmR);
  const elbowR = new THREE.Mesh(boxGeo7, jointMat);
  elbowR.name = 'elbowR';
  forearmR.add(elbowR);
  const lowerArmR = new THREE.Mesh(boxGeo8, boneMat);
  lowerArmR.name = 'lowerArmR';
  lowerArmR.position.set(0, -0.12, 0);
  forearmR.add(lowerArmR);
  const handR = new THREE.Mesh(boxGeo9, boneMat);
  handR.name = 'handR';
  handR.position.set(0, -0.25, 0);
  forearmR.add(handR);
  const weapon = new THREE.Group();
  weapon.name = 'weapon';
  weapon.position.set(0, -0.25, 0);
  weapon.rotation.set(deg(95), 0, 0);
  forearmR.add(weapon);
  const grip = new THREE.Mesh(new THREE.BoxGeometry(0.04, 0.16, 0.04), leatherMat);
  grip.name = 'grip';
  weapon.add(grip);
  const pommel = new THREE.Mesh(new THREE.BoxGeometry(0.055, 0.05, 0.055), ironMat);
  pommel.name = 'pommel';
  pommel.position.set(0, -0.1, 0);
  weapon.add(pommel);
  const crossguard = new THREE.Mesh(new THREE.BoxGeometry(0.24, 0.045, 0.06), ironMat);
  crossguard.name = 'crossguard';
  crossguard.position.set(0, 0.1, 0);
  weapon.add(crossguard);
  const blade = new THREE.Mesh(extrude([[-0.035, 0], [0.035, 0], [0.035, 0.5], [0, 0.58], [-0.035, 0.5]], 0.02), steelMat);
  blade.name = 'blade';
  blade.position.set(0, 0.12, 0);
  weapon.add(blade);
  const fuller = new THREE.Mesh(new THREE.BoxGeometry(0.012, 0.42, 0.024), ironMat);
  fuller.name = 'fuller';
  fuller.position.set(0, 0.36, 0);
  weapon.add(fuller);
  const head = new THREE.Group();
  head.name = 'head';
  head.position.set(0, 1.13, 0);
  root.add(head);
  const neck = new THREE.Mesh(new THREE.BoxGeometry(0.07, 0.1, 0.07), jointMat);
  neck.name = 'neck';
  neck.position.set(0, 0.03, -0.03);
  head.add(neck);
  const skull = new THREE.Mesh(new THREE.BoxGeometry(0.38, 0.3, 0.34), boneMat);
  skull.name = 'skull';
  skull.position.set(0, 0.22, 0.01);
  head.add(skull);
  const jaw = new THREE.Mesh(new THREE.BoxGeometry(0.32, 0.07, 0.28), boneMat);
  jaw.name = 'jaw';
  jaw.position.set(0, 0.03, 0.03);
  head.add(jaw);
  const mouthGap = new THREE.Mesh(new THREE.BoxGeometry(0.22, 0.035, 0.01), socketMat);
  mouthGap.name = 'mouthGap';
  mouthGap.position.set(0, 0.075, 0.176);
  head.add(mouthGap);
  const toothGap2 = new THREE.Mesh(boxGeo10, socketMat);
  toothGap2.name = 'toothGap2';
  toothGap2.position.set(-0.05, 0.04, 0.171);
  head.add(toothGap2);
  const toothGap3 = new THREE.Mesh(boxGeo10, socketMat);
  toothGap3.name = 'toothGap3';
  toothGap3.position.set(0, 0.04, 0.171);
  head.add(toothGap3);
  const toothGap4 = new THREE.Mesh(boxGeo10, socketMat);
  toothGap4.name = 'toothGap4';
  toothGap4.position.set(0.05, 0.04, 0.171);
  head.add(toothGap4);
  const socketL = new THREE.Mesh(boxGeo11, socketMat);
  socketL.name = 'socketL';
  socketL.position.set(0.085, 0.25, 0.175);
  head.add(socketL);
  const eyeGlowL = new THREE.Mesh(boxGeo12, glowMat);
  eyeGlowL.name = 'eyeGlowL';
  eyeGlowL.position.set(0.08, 0.245, 0.188);
  head.add(eyeGlowL);
  const socketR = new THREE.Mesh(boxGeo11, socketMat);
  socketR.name = 'socketR';
  socketR.position.set(-0.085, 0.25, 0.175);
  head.add(socketR);
  const eyeGlowR = new THREE.Mesh(boxGeo12, glowMat);
  eyeGlowR.name = 'eyeGlowR';
  eyeGlowR.position.set(-0.08, 0.245, 0.188);
  head.add(eyeGlowR);
  const noseHole = new THREE.Mesh(extrude([[-0.025, 0], [0.025, 0], [0, -0.04]], 0.02), socketMat);
  noseHole.name = 'noseHole';
  noseHole.position.set(0, 0.19, 0.175);
  head.add(noseHole);

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

export default createSkeletonWarrior;
```

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