# Picture Frame

> A wall-hung framed painting whose canvas is a tiny layered low-poly landscape: snow-capped mountain ranges, rolling hills, a lake, pine trees, clouds and a sun or moon. Choose a classic wooden frame with a gilded lip, an ornate frame with gold rosettes and crest, or a slim modern frame with a white mat, and a day, sunset or night scene. The back sits flush on the wall plane for houses, castles, galleries and offices.

![Picture Frame preview](https://codels.download/m/picture-frame.png)

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

## Facts

- Category: Props › Decor
- Size (W × H × D): 0.70 × 0.55 × 0.04 m — units are meters, +Y up, front faces +Z
- Origin: wall mount point (back of the model on the wall plane z = 0)
- Triangles: 472 · Meshes: 29 · Materials: 10
- License: CC0-1.0 (Codels built-in)
- Tags: house, painting, wall-decor, low-poly, stylized, fantasy, medieval, modern, castle, interior, decoration, prop, gold, wood, boxy-shape, flat, mobile-friendly, parametric, color-blue, color-brown, size-small, poly-low
- Page: https://codels.download/models/picture-frame

## Files

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

## Parameters

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

| Param | Type | Range / options | Default | Current |
|---|---|---|---|---|
| `style` | select | classic \| ornate \| modern | classic | classic |
| `scene` | select | day \| sunset \| night | day | day |
| `width` | number | 0.4 – 1.2 | 0.7 | 0.7 |
| `frame` | color | #rrggbb | #6b4528 | #6b4528 |

## Parts

- frame (extrude · frame)
- lip (extrude · gold)
- painting (group)
  - canvas (box · sky)
  - sun (cylinder · glow)
  - cloud (box · light)
  - cloud (box · light)
  - cloud (box · light)
  - cloud (box · light)
  - farRange (extrude · far)
  - farSnow (extrude · light)
  - farSnow (extrude · light)
  - nearRange (extrude · mountain)
  - nearRange (extrude · mountain)
  - nearSnow (extrude · light)
  - nearSnow (extrude · light)
  - hills (extrude · hill)
  - lake (extrude · water)
  - glint (box · light)
  - glint (box · light)
  - pine (extrude · tree)
  - pine (extrude · tree)
  - pine (extrude · tree)
  - pine (extrude · tree)
  - pine (extrude · tree)
  - pine (extrude · tree)
  - pine (extrude · tree)
  - pine (extrude · tree)
  - pine (extrude · tree)
  - pine (extrude · tree)

## How to use

Save the code below as `picture-frame.js`, then:

```js
import { createPictureFrame } from './picture-frame.js';
const model = createPictureFrame();
scene.add(model);
```

## Code

```js
/**
 * Picture Frame — Codels 3D model library (https://codels.download/models/picture-frame)
 * License: CC0-1.0 · Units: meters · Y-up · Front: +Z · Origin: wall mount point (back of the model on the wall plane z = 0)
 * Triangles: 472 · Size (W × H × D): 0.70 × 0.55 × 0.04 m
 *
 * Usage:
 *   import { createPictureFrame } from './picture-frame.js';
 *   
 *   const model = createPictureFrame();
 *   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 createPictureFrame() {
  const root = new THREE.Group();
  root.name = 'PictureFrame';

  // Materials
  const frameMat = new THREE.MeshStandardMaterial({ name: 'frame', color: 0x6b4528, roughness: 0.65, flatShading: true });
  const goldMat = new THREE.MeshStandardMaterial({ name: 'gold', color: 0xc9a043, roughness: 0.35, metalness: 0.7, flatShading: true });
  const skyMat = new THREE.MeshStandardMaterial({ name: 'sky', color: 0x9ccbe8, roughness: 0.9, flatShading: true });
  const glowMat = new THREE.MeshStandardMaterial({ name: 'glow', color: 0xfff1a6, roughness: 0.8, flatShading: true });
  const lightMat = new THREE.MeshStandardMaterial({ name: 'light', color: 0xffffff, roughness: 0.85, flatShading: true });
  const farMat = new THREE.MeshStandardMaterial({ name: 'far', color: 0x9aa6c0, roughness: 0.9, flatShading: true });
  const mountainMat = new THREE.MeshStandardMaterial({ name: 'mountain', color: 0x6c7a96, roughness: 0.9, flatShading: true });
  const hillMat = new THREE.MeshStandardMaterial({ name: 'hill', color: 0x7cab4c, roughness: 0.9, flatShading: true });
  const waterMat = new THREE.MeshStandardMaterial({ name: 'water', color: 0x4f93c9, roughness: 0.5, flatShading: true });
  const treeMat = new THREE.MeshStandardMaterial({ name: 'tree', color: 0x2f5e33, roughness: 0.9, flatShading: true });

  // Parts
  const frame = new THREE.Mesh(extrude([[-0.342, -0.265], [0.342, -0.265], [0.342, 0.265], [-0.342, 0.265]], 0.022, [[[-0.287, -0.21], [0.287, -0.21], [0.287, 0.21], [-0.287, 0.21]]], [0.007, 0.008, 1]), frameMat);
  frame.name = 'frame';
  frame.position.set(0, 0, 0.018);
  root.add(frame);
  const lip = new THREE.Mesh(extrude([[-0.283, -0.206], [0.283, -0.206], [0.283, 0.206], [-0.283, 0.206]], 0.004, [[[-0.267, -0.19], [0.267, -0.19], [0.267, 0.19], [-0.267, 0.19]]]), goldMat);
  lip.name = 'lip';
  lip.position.set(0, 0, 0.019);
  root.add(lip);
  const painting = new THREE.Group();
  painting.name = 'painting';
  painting.position.set(0, 0, 0.006);
  root.add(painting);
  const canvas = new THREE.Mesh(new THREE.BoxGeometry(0.558, 0.404, 0.008), skyMat);
  canvas.name = 'canvas';
  canvas.position.set(0, 0, -0.002);
  painting.add(canvas);
  const sun = new THREE.Mesh(new THREE.CylinderGeometry(0.0285, 0.0285, 0.001, 10), glowMat);
  sun.name = 'sun';
  sun.position.set(0.1335, 0.0988, 0.0025);
  sun.rotation.set(deg(90), 0, 0);
  painting.add(sun);
  const cloud = new THREE.Mesh(new THREE.BoxGeometry(0.0961, 0.0152, 0.001), lightMat);
  cloud.name = 'cloud';
  cloud.position.set(-0.1335, 0.1178, 0.003);
  painting.add(cloud);
  const cloud2 = new THREE.Mesh(new THREE.BoxGeometry(0.0481, 0.0285, 0.001), lightMat);
  cloud2.name = 'cloud';
  cloud2.position.set(-0.1442, 0.1254, 0.003);
  painting.add(cloud2);
  const cloud3 = new THREE.Mesh(new THREE.BoxGeometry(0.0673, 0.0106, 0.001), lightMat);
  cloud3.name = 'cloud';
  cloud3.position.set(0.032, 0.1482, 0.003);
  painting.add(cloud3);
  const cloud4 = new THREE.Mesh(new THREE.BoxGeometry(0.0336, 0.0199, 0.001), lightMat);
  cloud4.name = 'cloud';
  cloud4.position.set(0.0246, 0.1535, 0.003);
  painting.add(cloud4);
  const farRange = new THREE.Mesh(extrude([[-0.279, -0.0418], [-0.279, 0.0228], [-0.1869, 0.057], [-0.1335, 0.0342], [-0.0534, 0.0817], [0.0267, 0.038], [0.0934, 0.095], [0.1655, 0.0494], [0.2243, 0.0684], [0.279, 0.0342], [0.279, -0.0418]], 0.001), farMat);
  farRange.name = 'farRange';
  farRange.position.set(0, 0, 0.0035);
  painting.add(farRange);
  const farSnow = new THREE.Mesh(extrude([[-0.0534, 0.0817], [-0.0774, 0.0675], [-0.0534, 0.0735], [-0.0294, 0.0686]], 0.001), lightMat);
  farSnow.name = 'farSnow';
  farSnow.position.set(0, 0, 0.0042);
  painting.add(farSnow);
  const farSnow2 = new THREE.Mesh(extrude([[0.0934, 0.095], [0.0734, 0.0779], [0.0934, 0.0858], [0.1151, 0.0813]], 0.001), lightMat);
  farSnow2.name = 'farSnow';
  farSnow2.position.set(0, 0, 0.0042);
  painting.add(farSnow2);
  const nearRange = new THREE.Mesh(extrude([[-0.279, -0.0418], [-0.279, 0], [-0.1655, 0.0722], [-0.1121, 0.0285], [-0.0694, 0.0494], [0.016, -0.0418]], 0.001), mountainMat);
  nearRange.name = 'nearRange';
  nearRange.position.set(0, 0, 0.005);
  painting.add(nearRange);
  const nearRange2 = new THREE.Mesh(extrude([[0.0748, -0.0418], [0.1602, 0.0456], [0.2083, 0.0152], [0.279, 0.0342], [0.279, -0.0418]], 0.001), mountainMat);
  nearRange2.name = 'nearRange';
  nearRange2.position.set(0, 0, 0.005);
  painting.add(nearRange2);
  const nearSnow = new THREE.Mesh(extrude([[-0.1655, 0.0722], [-0.2041, 0.0477], [-0.1655, 0.0604], [-0.1474, 0.0573]], 0.001), lightMat);
  nearSnow.name = 'nearSnow';
  nearSnow.position.set(0, 0, 0.0058);
  painting.add(nearSnow);
  const nearSnow2 = new THREE.Mesh(extrude([[0.1602, 0.0456], [0.1329, 0.0176], [0.1602, 0.0343], [0.1756, 0.0359]], 0.001), lightMat);
  nearSnow2.name = 'nearSnow';
  nearSnow2.position.set(0, 0, 0.0058);
  painting.add(nearSnow2);
  const hills = new THREE.Mesh(extrude([[-0.279, -0.202], [0.279, -0.202], [0.279, -0.0152], [0.1976, 0], [0.1201, -0.0209], [0.0374, 0.0076], [-0.0534, -0.0247], [-0.1388, -0.0038], [-0.279, -0.019]], 0.001), hillMat);
  hills.name = 'hills';
  hills.position.set(0, 0, 0.007);
  painting.add(hills);
  const lake = new THREE.Mesh(extrude([[0.0053, -0.1064], [0.1495, -0.1178], [0.1976, -0.0836], [0.1068, -0.0608], [0.0214, -0.0722]], 0.001), waterMat);
  lake.name = 'lake';
  lake.position.set(0, 0, 0.008);
  painting.add(lake);
  const glint = new THREE.Mesh(new THREE.BoxGeometry(0.0481, 0.0023, 0.001), lightMat);
  glint.name = 'glint';
  glint.position.set(0.0854, -0.0836, 0.009);
  painting.add(glint);
  const glint2 = new THREE.Mesh(new THREE.BoxGeometry(0.0267, 0.0023, 0.001), lightMat);
  glint2.name = 'glint';
  glint2.position.set(0.1335, -0.0988, 0.009);
  painting.add(glint2);
  const pine = new THREE.Mesh(extrude([[-0.2105, -0.0646], [-0.174, -0.0646], [-0.1922, -0.0293]], 0.001), treeMat);
  pine.name = 'pine';
  pine.position.set(0, 0, 0.0095);
  painting.add(pine);
  const pine2 = new THREE.Mesh(extrude([[-0.2059, -0.0429], [-0.1786, -0.0429], [-0.1922, -0.0076]], 0.001), treeMat);
  pine2.name = 'pine';
  pine2.position.set(0, 0, 0.0095);
  painting.add(pine2);
  const pine3 = new THREE.Mesh(extrude([[-0.1726, -0.095], [-0.1264, -0.095], [-0.1495, -0.0502]], 0.001), treeMat);
  pine3.name = 'pine';
  pine3.position.set(0, 0, 0.0095);
  painting.add(pine3);
  const pine4 = new THREE.Mesh(extrude([[-0.1668, -0.0676], [-0.1322, -0.0676], [-0.1495, -0.0228]], 0.001), treeMat);
  pine4.name = 'pine';
  pine4.position.set(0, 0, 0.0095);
  painting.add(pine4);
  const pine5 = new THREE.Mesh(extrude([[-0.251, -0.1292], [-0.1975, -0.1292], [-0.2243, -0.0774]], 0.001), treeMat);
  pine5.name = 'pine';
  pine5.position.set(0, 0, 0.0095);
  painting.add(pine5);
  const pine6 = new THREE.Mesh(extrude([[-0.2443, -0.0974], [-0.2042, -0.0974], [-0.2243, -0.0456]], 0.001), treeMat);
  pine6.name = 'pine';
  pine6.position.set(0, 0, 0.0095);
  painting.add(pine6);
  const pine7 = new THREE.Mesh(extrude([[-0.1042, -0.057], [-0.0774, -0.057], [-0.0908, -0.0311]], 0.001), treeMat);
  pine7.name = 'pine';
  pine7.position.set(0, 0, 0.0095);
  painting.add(pine7);
  const pine8 = new THREE.Mesh(extrude([[-0.1008, -0.0411], [-0.0807, -0.0411], [-0.0908, -0.0152]], 0.001), treeMat);
  pine8.name = 'pine';
  pine8.position.set(0, 0, 0.0095);
  painting.add(pine8);
  const pine9 = new THREE.Mesh(extrude([[0.2138, -0.0532], [0.2454, -0.0532], [0.2296, -0.0226]], 0.001), treeMat);
  pine9.name = 'pine';
  pine9.position.set(0, 0, 0.0095);
  painting.add(pine9);
  const pine10 = new THREE.Mesh(extrude([[0.2178, -0.0344], [0.2415, -0.0344], [0.2296, -0.0038]], 0.001), treeMat);
  pine10.name = 'pine';
  pine10.position.set(0, 0, 0.0095);
  painting.add(pine10);

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

export default createPictureFrame;
```

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