# Wooden Bridge

> An arched wooden footbridge with individually laid deck planks on curved stringers, stone abutments, railing posts and either sturdy wooden handrails or sagging rope railings. Set the span length, deck width and arch height to cross streams, rivers and ravines in fantasy, farm and adventure levels.

![Wooden Bridge preview](https://codels.download/m/wooden-bridge.png)

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

## Facts

- Category: Buildings › Structures
- Size (W × H × D): 7.50 × 2.02 × 2.32 m — units are meters, +Y up, front faces +Z
- Origin: center of the base (y = 0)
- Triangles: 1,092 · Meshes: 73 · Materials: 4
- License: CC0-1.0 (Codels built-in)
- Tags: bridge, footbridge, low-poly, stylized, fantasy, japanese, forest, garden, river, village, adventure, platformer, rpg, building, modular, platform, stone, wood, mobile-friendly, parametric, color-brown, color-gray, size-large, poly-low
- Page: https://codels.download/models/wooden-bridge

## Files

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

## Parameters

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

| Param | Type | Range / options | Default | Current |
|---|---|---|---|---|
| `length` | number | 3 – 12 | 6 | 6 |
| `width` | number | 1.2 – 3 | 1.8 | 1.8 |
| `arch` | number | 0 – 1.6 | 0.6 | 0.6 |
| `railing` | select | wood \| rope | wood | wood |

## Parts

- abutment (box · stone)
- abutmentStep (box · stone)
- abutment (box · stone)
- abutmentStep (box · stone)
- stringer (tube · beam)
- stringer (tube · beam)
- strut (box · beam)
- strut (box · beam)
- strut (box · beam)
- strut (box · beam)
- plank (box · plank)
- plank (box · plank2)
- plank (box · plank)
- plank (box · plank)
- plank (box · plank)
- plank (box · plank2)
- plank (box · plank)
- plank (box · plank2)
- plank (box · plank2)
- plank (box · plank)
- plank (box · plank2)
- plank (box · plank2)
- plank (box · plank2)
- plank (box · plank)
- plank (box · plank)
- plank (box · plank)
- plank (box · plank)
- plank (box · plank)
- plank (box · plank)
- plank (box · plank)
- plank (box · plank)
- plank (box · plank2)
- plank (box · plank)
- plank (box · plank2)
- plank (box · plank2)
- endPost (box · beam)
- postCap (cylinder · beam)
- post (box · beam)
- post (box · beam)
- post (box · beam)

## How to use

Save the code below as `wooden-bridge.js`, then:

```js
import { createWoodenBridge } from './wooden-bridge.js';
const model = createWoodenBridge();
scene.add(model);
```

## Code

```js
/**
 * Wooden Bridge — Codels 3D model library (https://codels.download/models/wooden-bridge)
 * License: CC0-1.0 · Units: meters · Y-up · Front: +Z · Origin: center of the base (y = 0)
 * Triangles: 1,092 · Size (W × H × D): 7.50 × 2.02 × 2.32 m
 *
 * Usage:
 *   import { createWoodenBridge } from './wooden-bridge.js';
 *   
 *   const model = createWoodenBridge();
 *   model.position.set(0, 0, 0);
 *   scene.add(model);
 */
import * as THREE from 'three';

const deg = THREE.MathUtils.degToRad;

// Deterministic vertex displacement (organic low-poly look).
function jitter(geometry, amount, seed) {
  const pos = geometry.attributes.position;
  const h = (a, b, c, s) => {
    let x = Math.imul(a, 73856093) ^ Math.imul(b, 19349663) ^ Math.imul(c, 83492791) ^ Math.imul(s, 2654435761);
    x = Math.imul(x ^ (x >>> 16), 2246822507);
    x = Math.imul(x ^ (x >>> 13), 3266489909);
    return ((x ^ (x >>> 16)) >>> 0) / 4294967295 - 0.5;
  };
  for (let i = 0; i < pos.count; i++) {
    const x = pos.getX(i), y = pos.getY(i), z = pos.getZ(i);
    const qx = Math.round(x * 1000), qy = Math.round(y * 1000), qz = Math.round(z * 1000);
    pos.setXYZ(i, x + h(qx, qy, qz, seed) * 2 * amount, y + h(qx, qy, qz, seed + 1) * 2 * amount, z + h(qx, qy, qz, seed + 2) * 2 * amount);
  }
  pos.needsUpdate = true;
  geometry.computeVertexNormals();
  return geometry;
}

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

  // Materials
  const plankMat = new THREE.MeshStandardMaterial({ name: 'plank', color: 0xa5743f, roughness: 0.9, flatShading: true });
  const plank2Mat = new THREE.MeshStandardMaterial({ name: 'plank2', color: 0x8c6033, roughness: 0.9, flatShading: true });
  const beamMat = new THREE.MeshStandardMaterial({ name: 'beam', color: 0x6a4528, roughness: 0.95, flatShading: true });
  const stoneMat = new THREE.MeshStandardMaterial({ name: 'stone', color: 0x75706a, roughness: 0.95, flatShading: true });

  // Shared geometries
  const boxGeo = new THREE.BoxGeometry(0.12, 1.2583, 0.12);
  const boxGeo2 = new THREE.BoxGeometry(0.215, 0.07, 1.7838);
  const boxGeo3 = new THREE.BoxGeometry(0.15, 1.32, 0.15);
  const cylinderGeo = new THREE.CylinderGeometry(0, 0.1125, 0.12, 4);
  const boxGeo4 = new THREE.BoxGeometry(0.1, 1.2, 0.1);
  const boxGeo5 = new THREE.BoxGeometry(0.07, 1.4817, 0.1);
  const boxGeo6 = new THREE.BoxGeometry(0.05, 1.4817, 0.05);
  const boxGeo7 = new THREE.BoxGeometry(0.04, 1.6323, 0.04);
  const boxGeo8 = new THREE.BoxGeometry(0.07, 1.4314, 0.1);
  const boxGeo9 = new THREE.BoxGeometry(0.05, 1.4314, 0.05);
  const boxGeo10 = new THREE.BoxGeometry(0.04, 1.5188, 0.04);
  const boxGeo11 = new THREE.BoxGeometry(0.04, 1.4476, 0.04);
  const boxGeo12 = new THREE.BoxGeometry(0.04, 1.4251, 0.04);

  // Parts
  const abutment = new THREE.Mesh(jitter(new THREE.BoxGeometry(1, 0.32, 2.3), 0.015, 3), stoneMat);
  abutment.name = 'abutment';
  abutment.position.set(-2.75, 0.172, 0);
  root.add(abutment);
  const abutmentStep = new THREE.Mesh(jitter(new THREE.BoxGeometry(0.6, 0.17, 2.1), 0.015, 5), stoneMat);
  abutmentStep.name = 'abutmentStep';
  abutmentStep.position.set(-3.45, 0.097, 0);
  root.add(abutmentStep);
  const abutment2 = new THREE.Mesh(jitter(new THREE.BoxGeometry(1, 0.32, 2.3), 0.015, 2), stoneMat);
  abutment2.name = 'abutment';
  abutment2.position.set(2.75, 0.172, 0);
  root.add(abutment2);
  const abutmentStep2 = new THREE.Mesh(jitter(new THREE.BoxGeometry(0.6, 0.17, 2.1), 0.015, 4), stoneMat);
  abutmentStep2.name = 'abutmentStep';
  abutmentStep2.position.set(3.45, 0.097, 0);
  root.add(abutmentStep2);
  const stringer = new THREE.Mesh(tube([[-2.95, 0.2698, -0.72], [-2.2125, 0.5237, -0.72], [-1.475, 0.705, -0.72], [-0.7375, 0.8137, -0.72], [0, 0.85, -0.72], [0.7375, 0.8137, -0.72], [1.475, 0.705, -0.72], [2.2125, 0.5237, -0.72], [2.95, 0.2698, -0.72]], 0.1, 16, 4), beamMat);
  stringer.name = 'stringer';
  root.add(stringer);
  const stringer2 = new THREE.Mesh(tube([[-2.95, 0.2698, 0.72], [-2.2125, 0.5237, 0.72], [-1.475, 0.705, 0.72], [-0.7375, 0.8137, 0.72], [0, 0.85, 0.72], [0.7375, 0.8137, 0.72], [1.475, 0.705, 0.72], [2.2125, 0.5237, 0.72], [2.95, 0.2698, 0.72]], 0.1, 16, 4), beamMat);
  stringer2.name = 'stringer';
  root.add(stringer2);
  const strut = new THREE.Mesh(boxGeo, beamMat);
  strut.name = 'strut';
  strut.position.set(-1.95, 0.4893, -0.72);
  strut.rotation.set(0, deg(180), deg(72.4939));
  root.add(strut);
  const strut2 = new THREE.Mesh(boxGeo, beamMat);
  strut2.name = 'strut';
  strut2.position.set(-1.95, 0.4893, 0.72);
  strut2.rotation.set(0, deg(180), deg(72.4939));
  root.add(strut2);
  const strut3 = new THREE.Mesh(boxGeo, beamMat);
  strut3.name = 'strut';
  strut3.position.set(1.95, 0.4893, -0.72);
  strut3.rotation.set(0, 0, deg(72.4939));
  root.add(strut3);
  const strut4 = new THREE.Mesh(boxGeo, beamMat);
  strut4.name = 'strut';
  strut4.position.set(1.95, 0.4893, 0.72);
  strut4.rotation.set(0, 0, deg(72.4939));
  root.add(strut4);
  const plank = new THREE.Mesh(new THREE.BoxGeometry(0.215, 0.07, 1.808), plankMat);
  plank.name = 'plank';
  plank.position.set(-2.88, 0.4269, -0.0271);
  plank.rotation.set(0, deg(-1.1117), deg(21.0068));
  root.add(plank);
  const plank2 = new THREE.Mesh(new THREE.BoxGeometry(0.215, 0.07, 1.7408), plank2Mat);
  plank2.name = 'plank';
  plank2.position.set(-2.64, 0.5186, 0.0035);
  plank2.rotation.set(0, deg(0.8894), deg(19.3921));
  root.add(plank2);
  const plank3 = new THREE.Mesh(new THREE.BoxGeometry(0.215, 0.07, 1.8187), plankMat);
  plank3.name = 'plank';
  plank3.position.set(-2.4, 0.5985, -0.02);
  plank3.rotation.set(0, deg(0.1457), deg(17.7447));
  root.add(plank3);
  const plank4 = new THREE.Mesh(boxGeo2, plankMat);
  plank4.name = 'plank';
  plank4.position.set(-2.16, 0.6753, -0.0226);
  plank4.rotation.set(0, deg(0.3515), deg(16.0664));
  root.add(plank4);
  const plank5 = new THREE.Mesh(new THREE.BoxGeometry(0.215, 0.07, 1.7921), plankMat);
  plank5.name = 'plank';
  plank5.position.set(-1.92, 0.7373, -0.0238);
  plank5.rotation.set(0, deg(0.59), deg(14.3593));
  root.add(plank5);
  const plank6 = new THREE.Mesh(new THREE.BoxGeometry(0.215, 0.07, 1.7884), plank2Mat);
  plank6.name = 'plank';
  plank6.position.set(-1.68, 0.7946, -0.0276);
  plank6.rotation.set(0, deg(-1.0527), deg(12.6258));
  root.add(plank6);
  const plank7 = new THREE.Mesh(new THREE.BoxGeometry(0.215, 0.07, 1.7747), plankMat);
  plank7.name = 'plank';
  plank7.position.set(-1.44, 0.8451, 0.0135);
  plank7.rotation.set(0, deg(-0.671), deg(10.8685));
  root.add(plank7);
  const plank8 = new THREE.Mesh(boxGeo2, plank2Mat);
  plank8.name = 'plank';
  plank8.position.set(-1.2, 0.8914, -0.0289);
  plank8.rotation.set(0, deg(0.0716), deg(9.0903));
  root.add(plank8);
  const plank9 = new THREE.Mesh(new THREE.BoxGeometry(0.215, 0.07, 1.7499), plank2Mat);
  plank9.name = 'plank';
  plank9.position.set(-0.96, 0.9225, -0.0229);
  plank9.rotation.set(0, deg(-0.7878), deg(7.2942));
  root.add(plank9);
  const plank10 = new THREE.Mesh(new THREE.BoxGeometry(0.215, 0.07, 1.7715), plankMat);
  plank10.name = 'plank';
  plank10.position.set(-0.72, 0.9458, -0.0198);
  plank10.rotation.set(0, deg(0.6217), deg(5.4836));
  root.add(plank10);
  const plank11 = new THREE.Mesh(new THREE.BoxGeometry(0.215, 0.07, 1.7592), plank2Mat);
  plank11.name = 'plank';
  plank11.position.set(-0.48, 0.9668, 0.024);
  plank11.rotation.set(0, deg(1.1897), deg(3.6619));
  root.add(plank11);
  const plank12 = new THREE.Mesh(new THREE.BoxGeometry(0.215, 0.07, 1.8319), plank2Mat);
  plank12.name = 'plank';
  plank12.position.set(-0.24, 0.9797, -0.0272);
  plank12.rotation.set(0, deg(0.6687), deg(1.8328));
  root.add(plank12);
  const plank13 = new THREE.Mesh(new THREE.BoxGeometry(0.215, 0.07, 1.7579), plank2Mat);
  plank13.name = 'plank';
  plank13.position.set(0, 0.9807, 0.0118);
  plank13.rotation.set(0, deg(0.7961), 0);
  root.add(plank13);
  const plank14 = new THREE.Mesh(new THREE.BoxGeometry(0.215, 0.07, 1.8561), plankMat);
  plank14.name = 'plank';
  plank14.position.set(0.24, 0.98, 0.0118);
  plank14.rotation.set(0, deg(0.9706), deg(-1.8328));
  root.add(plank14);
  const plank15 = new THREE.Mesh(new THREE.BoxGeometry(0.215, 0.07, 1.8171), plankMat);
  plank15.name = 'plank';
  plank15.position.set(0.48, 0.9722, 0.0254);
  plank15.rotation.set(0, deg(0.0885), deg(-3.6619));
  root.add(plank15);
  const plank16 = new THREE.Mesh(new THREE.BoxGeometry(0.215, 0.07, 1.8331), plankMat);
  plank16.name = 'plank';
  plank16.position.set(0.72, 0.9546, -0.0138);
  plank16.rotation.set(0, deg(0.352), deg(-5.4836));
  root.add(plank16);
  const plank17 = new THREE.Mesh(new THREE.BoxGeometry(0.215, 0.07, 1.765), plankMat);
  plank17.name = 'plank';
  plank17.position.set(0.96, 0.9251, -0.0058);
  plank17.rotation.set(0, deg(-0.9303), deg(-7.2942));
  root.add(plank17);
  const plank18 = new THREE.Mesh(new THREE.BoxGeometry(0.215, 0.07, 1.7487), plankMat);
  plank18.name = 'plank';
  plank18.position.set(1.2, 0.8844, 0.0256);
  plank18.rotation.set(0, deg(0.6926), deg(-9.0903));
  root.add(plank18);
  const plank19 = new THREE.Mesh(new THREE.BoxGeometry(0.215, 0.07, 1.7831), plankMat);
  plank19.name = 'plank';
  plank19.position.set(1.44, 0.8516, -0.0212);
  plank19.rotation.set(0, deg(-0.3655), deg(-10.8685));
  root.add(plank19);
  const plank20 = new THREE.Mesh(new THREE.BoxGeometry(0.215, 0.07, 1.7563), plankMat);
  plank20.name = 'plank';
  plank20.position.set(1.68, 0.7948, -0.0075);
  plank20.rotation.set(0, deg(-0.7818), deg(-12.6258));
  root.add(plank20);
  const plank21 = new THREE.Mesh(new THREE.BoxGeometry(0.215, 0.07, 1.8151), plankMat);
  plank21.name = 'plank';
  plank21.position.set(1.92, 0.7371, 0.0206);
  plank21.rotation.set(0, deg(1.103), deg(-14.3593));
  root.add(plank21);
  const plank22 = new THREE.Mesh(new THREE.BoxGeometry(0.215, 0.07, 1.8089), plank2Mat);
  plank22.name = 'plank';
  plank22.position.set(2.16, 0.6691, 0.0004);
  plank22.rotation.set(0, deg(-0.7146), deg(-16.0664));
  root.add(plank22);
  const plank23 = new THREE.Mesh(new THREE.BoxGeometry(0.215, 0.07, 1.8411), plankMat);
  plank23.name = 'plank';
  plank23.position.set(2.4, 0.6054, 0.0152);
  plank23.rotation.set(0, deg(0.9122), deg(-17.7447));
  root.add(plank23);
  const plank24 = new THREE.Mesh(new THREE.BoxGeometry(0.215, 0.07, 1.8455), plank2Mat);
  plank24.name = 'plank';
  plank24.position.set(2.64, 0.5249, 0.0147);
  plank24.rotation.set(0, deg(-1.0455), deg(-19.3921));
  root.add(plank24);
  const plank25 = new THREE.Mesh(new THREE.BoxGeometry(0.215, 0.07, 1.754), plank2Mat);
  plank25.name = 'plank';
  plank25.position.set(2.88, 0.4358, -0.021);
  plank25.rotation.set(0, deg(-0.3847), deg(-21.0068));
  root.add(plank25);
  const endPost = new THREE.Mesh(boxGeo3, beamMat);
  endPost.name = 'endPost';
  endPost.position.set(-2.85, 0.9385, -0.88);
  root.add(endPost);
  const postCap = new THREE.Mesh(cylinderGeo, beamMat);
  postCap.name = 'postCap';
  postCap.position.set(-2.85, 1.6585, -0.88);
  postCap.rotation.set(0, deg(45), 0);
  root.add(postCap);
  const post = new THREE.Mesh(boxGeo4, beamMat);
  post.name = 'post';
  post.position.set(-1.425, 1.2846, -0.88);
  root.add(post);
  const post2 = new THREE.Mesh(boxGeo4, beamMat);
  post2.name = 'post';
  post2.position.set(0, 1.42, -0.88);
  root.add(post2);
  const post3 = new THREE.Mesh(boxGeo4, beamMat);
  post3.name = 'post';
  post3.position.set(1.425, 1.2846, -0.88);
  root.add(post3);
  const endPost2 = new THREE.Mesh(boxGeo3, beamMat);
  endPost2.name = 'endPost';
  endPost2.position.set(2.85, 0.9385, -0.88);
  root.add(endPost2);
  const postCap2 = new THREE.Mesh(cylinderGeo, beamMat);
  postCap2.name = 'postCap';
  postCap2.position.set(2.85, 1.6585, -0.88);
  postCap2.rotation.set(0, deg(45), 0);
  root.add(postCap2);
  const handrail = new THREE.Mesh(boxGeo5, plankMat);
  handrail.name = 'handrail';
  handrail.position.set(-2.1375, 1.6316, -0.88);
  handrail.rotation.set(0, deg(180), deg(74.0924));
  root.add(handrail);
  const midRail = new THREE.Mesh(boxGeo6, plank2Mat);
  midRail.name = 'midRail';
  midRail.position.set(-2.1375, 1.1816, -0.88);
  midRail.rotation.set(0, deg(180), deg(74.0924));
  root.add(midRail);
  const infill = new THREE.Mesh(boxGeo7, beamMat);
  infill.name = 'infill';
  infill.position.set(-2.1375, 0.9566, -0.88);
  infill.rotation.set(0, deg(180), deg(60.8086));
  root.add(infill);
  const handrail2 = new THREE.Mesh(boxGeo8, plankMat);
  handrail2.name = 'handrail';
  handrail2.position.set(-0.7125, 1.9023, -0.88);
  handrail2.rotation.set(0, deg(180), deg(84.5732));
  root.add(handrail2);
  const midRail2 = new THREE.Mesh(boxGeo9, plank2Mat);
  midRail2.name = 'midRail';
  midRail2.position.set(-0.7125, 1.4523, -0.88);
  midRail2.rotation.set(0, deg(180), deg(84.5732));
  root.add(midRail2);
  const infill2 = new THREE.Mesh(boxGeo10, beamMat);
  infill2.name = 'infill';
  infill2.position.set(-0.7125, 1.2273, -0.88);
  infill2.rotation.set(0, deg(180), deg(69.7619));
  root.add(infill2);
  const handrail3 = new THREE.Mesh(boxGeo8, plankMat);
  handrail3.name = 'handrail';
  handrail3.position.set(0.7125, 1.9023, -0.88);
  handrail3.rotation.set(0, deg(180), deg(95.4268));
  root.add(handrail3);
  const midRail3 = new THREE.Mesh(boxGeo9, plank2Mat);
  midRail3.name = 'midRail';
  midRail3.position.set(0.7125, 1.4523, -0.88);
  midRail3.rotation.set(0, deg(180), deg(95.4268));
  root.add(midRail3);
  const infill3 = new THREE.Mesh(boxGeo11, beamMat);
  infill3.name = 'infill';
  infill3.position.set(0.7125, 1.2273, -0.88);
  infill3.rotation.set(0, deg(180), deg(79.8691));
  root.add(infill3);
  const handrail4 = new THREE.Mesh(boxGeo5, plankMat);
  handrail4.name = 'handrail';
  handrail4.position.set(2.1375, 1.6316, -0.88);
  handrail4.rotation.set(0, deg(180), deg(105.9076));
  root.add(handrail4);
  const midRail4 = new THREE.Mesh(boxGeo6, plank2Mat);
  midRail4.name = 'midRail';
  midRail4.position.set(2.1375, 1.1816, -0.88);
  midRail4.rotation.set(0, deg(180), deg(105.9076));
  root.add(midRail4);
  const infill4 = new THREE.Mesh(boxGeo12, beamMat);
  infill4.name = 'infill';
  infill4.position.set(2.1375, 0.9566, -0.88);
  infill4.rotation.set(0, deg(180), deg(90.6483));
  root.add(infill4);
  const endPost3 = new THREE.Mesh(boxGeo3, beamMat);
  endPost3.name = 'endPost';
  endPost3.position.set(-2.85, 0.9385, 0.88);
  root.add(endPost3);
  const postCap3 = new THREE.Mesh(cylinderGeo, beamMat);
  postCap3.name = 'postCap';
  postCap3.position.set(-2.85, 1.6585, 0.88);
  postCap3.rotation.set(0, deg(45), 0);
  root.add(postCap3);
  const post4 = new THREE.Mesh(boxGeo4, beamMat);
  post4.name = 'post';
  post4.position.set(-1.425, 1.2846, 0.88);
  root.add(post4);
  const post5 = new THREE.Mesh(boxGeo4, beamMat);
  post5.name = 'post';
  post5.position.set(0, 1.42, 0.88);
  root.add(post5);
  const post6 = new THREE.Mesh(boxGeo4, beamMat);
  post6.name = 'post';
  post6.position.set(1.425, 1.2846, 0.88);
  root.add(post6);
  const endPost4 = new THREE.Mesh(boxGeo3, beamMat);
  endPost4.name = 'endPost';
  endPost4.position.set(2.85, 0.9385, 0.88);
  root.add(endPost4);
  const postCap4 = new THREE.Mesh(cylinderGeo, beamMat);
  postCap4.name = 'postCap';
  postCap4.position.set(2.85, 1.6585, 0.88);
  postCap4.rotation.set(0, deg(45), 0);
  root.add(postCap4);
  const handrail5 = new THREE.Mesh(boxGeo5, plankMat);
  handrail5.name = 'handrail';
  handrail5.position.set(-2.1375, 1.6316, 0.88);
  handrail5.rotation.set(0, deg(180), deg(74.0924));
  root.add(handrail5);
  const midRail5 = new THREE.Mesh(boxGeo6, plank2Mat);
  midRail5.name = 'midRail';
  midRail5.position.set(-2.1375, 1.1816, 0.88);
  midRail5.rotation.set(0, deg(180), deg(74.0924));
  root.add(midRail5);
  const infill5 = new THREE.Mesh(boxGeo7, beamMat);
  infill5.name = 'infill';
  infill5.position.set(-2.1375, 0.9566, 0.88);
  infill5.rotation.set(0, deg(180), deg(60.8086));
  root.add(infill5);
  const handrail6 = new THREE.Mesh(boxGeo8, plankMat);
  handrail6.name = 'handrail';
  handrail6.position.set(-0.7125, 1.9023, 0.88);
  handrail6.rotation.set(0, deg(180), deg(84.5732));
  root.add(handrail6);
  const midRail6 = new THREE.Mesh(boxGeo9, plank2Mat);
  midRail6.name = 'midRail';
  midRail6.position.set(-0.7125, 1.4523, 0.88);
  midRail6.rotation.set(0, deg(180), deg(84.5732));
  root.add(midRail6);
  const infill6 = new THREE.Mesh(boxGeo10, beamMat);
  infill6.name = 'infill';
  infill6.position.set(-0.7125, 1.2273, 0.88);
  infill6.rotation.set(0, deg(180), deg(69.7619));
  root.add(infill6);
  const handrail7 = new THREE.Mesh(boxGeo8, plankMat);
  handrail7.name = 'handrail';
  handrail7.position.set(0.7125, 1.9023, 0.88);
  handrail7.rotation.set(0, deg(180), deg(95.4268));
  root.add(handrail7);
  const midRail7 = new THREE.Mesh(boxGeo9, plank2Mat);
  midRail7.name = 'midRail';
  midRail7.position.set(0.7125, 1.4523, 0.88);
  midRail7.rotation.set(0, deg(180), deg(95.4268));
  root.add(midRail7);
  const infill7 = new THREE.Mesh(boxGeo11, beamMat);
  infill7.name = 'infill';
  infill7.position.set(0.7125, 1.2273, 0.88);
  infill7.rotation.set(0, deg(180), deg(79.8691));
  root.add(infill7);
  const handrail8 = new THREE.Mesh(boxGeo5, plankMat);
  handrail8.name = 'handrail';
  handrail8.position.set(2.1375, 1.6316, 0.88);
  handrail8.rotation.set(0, deg(180), deg(105.9076));
  root.add(handrail8);
  const midRail8 = new THREE.Mesh(boxGeo6, plank2Mat);
  midRail8.name = 'midRail';
  midRail8.position.set(2.1375, 1.1816, 0.88);
  midRail8.rotation.set(0, deg(180), deg(105.9076));
  root.add(midRail8);
  const infill8 = new THREE.Mesh(boxGeo12, beamMat);
  infill8.name = 'infill';
  infill8.position.set(2.1375, 0.9566, 0.88);
  infill8.rotation.set(0, deg(180), deg(90.6483));
  root.add(infill8);

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

export default createWoodenBridge;
```

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