[object Object]

← back to Quadrille Showroom

auto-save: 2026-07-02T09:46:55 (1 files) — public/js/showroom.js

646f4b9f5e2389fbdfd4db60faf6ca3039271ca5 · 2026-07-02 09:47:02 -0700 · Steve Abrams

Files touched

Diff

commit 646f4b9f5e2389fbdfd4db60faf6ca3039271ca5
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Jul 2 09:47:02 2026 -0700

    auto-save: 2026-07-02T09:46:55 (1 files) — public/js/showroom.js
---
 public/js/showroom.js | 115 ++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 84 insertions(+), 31 deletions(-)

diff --git a/public/js/showroom.js b/public/js/showroom.js
index ef3b152..3cf49c4 100644
--- a/public/js/showroom.js
+++ b/public/js/showroom.js
@@ -1036,13 +1036,34 @@ function buildTaperedLeg(parent, x, z, mat, h) {
   parent.add(leg);
 }
 
+// Béton-brut board-form lines: thin recessed vertical grooves on the two broad faces of a
+// concrete block, so a solid plinth reads as poured-and-formed concrete (not styrofoam).
+// Cheap — a handful of dark inset strips proud of ±z faces at (cx,cy,cz), block w×h×d.
+function buildBoardFormLines(parent, cx, cy, cz, w, h, d, mat, count) {
+  count = count || 3;
+  for (let i = 0; i < count; i++) {
+    const gx = -w / 2 + (w * (i + 1)) / (count + 1);
+    [1, -1].forEach(s => {
+      const groove = new THREE.Mesh(new THREE.BoxGeometry(0.012, h * 0.92, 0.012), mat);
+      groove.position.set(cx + gx, cy, cz + s * (d / 2 - 0.004));
+      parent.add(groove);
+    });
+  }
+  // one horizontal form-board seam across the front face
+  const seam = new THREE.Mesh(new THREE.BoxGeometry(w * 0.96, 0.012, 0.012), mat);
+  seam.position.set(cx, cy + h * 0.12, cz + d / 2 - 0.004);
+  parent.add(seam);
+}
+
 const FURNITURE_STYLES = {
   // CONTEMPORARY — clean, low, rectilinear; pale oak top on minimal charcoal blade legs.
   contemporary: {
     label: 'Contemporary',
     buildTable(g) {
-      const oak = fmat(0xc2ad86, 0.5, 0.0), blade = fmat(0x35352f, 0.7, 0.05);
-      const top = new THREE.Mesh(new THREE.BoxGeometry(1.12, 0.05, 0.70), oak);
+      // Crisp WHITE lacquer top on steel blade legs — deliberately cool/white so it never
+      // reads as the warm wood of the Transitional set beside it.
+      const white = fmat(0xecebe6, 0.35, 0.0), blade = fmat(0x484a4e, 0.45, 0.55);
+      const top = new THREE.Mesh(new THREE.BoxGeometry(1.12, 0.05, 0.70), white);
       top.position.set(0, 0.72, 0); top.castShadow = true; top.receiveShadow = true; g.add(top);
       [[-0.50, -0.30], [0.50, -0.30], [-0.50, 0.30], [0.50, 0.30]].forEach(([lx, lz]) => {
         const leg = new THREE.Mesh(new THREE.BoxGeometry(0.045, 0.70, 0.045), blade);
@@ -1051,7 +1072,7 @@ const FURNITURE_STYLES = {
       furnitureShadow(g, 0, 0, 1.35, 0.95, 0.6);
     },
     buildChair(g) {
-      const seatMat = fmat(0xcfcabf, 0.9, 0.0), blade = fmat(0x35352f, 0.7, 0.05);
+      const seatMat = fmat(0xe4e1d8, 0.55, 0.0), blade = fmat(0x484a4e, 0.45, 0.55);
       const c = new THREE.Group(); c.position.set(0, 0, 0.68);   // faces -z (toward table/boards)
       const seat = new THREE.Mesh(new THREE.BoxGeometry(0.48, 0.08, 0.46), seatMat);
       seat.position.set(0, 0.44, 0); seat.castShadow = true; seat.receiveShadow = true; c.add(seat);
@@ -1078,32 +1099,47 @@ const FURNITURE_STYLES = {
   traditional: {
     label: 'Traditional',
     buildTable(g) {
-      const walnut = fmat(0x5a3a24, 0.45, 0.0);
+      const walnut = fmat(0x3a2712, 0.45, 0.0);
       const top = new THREE.Mesh(new THREE.BoxGeometry(1.06, 0.06, 0.70), walnut);
       top.position.set(0, 0.74, 0); top.castShadow = true; top.receiveShadow = true; g.add(top);
       // a subtle apron under the top for weight
-      const apron = new THREE.Mesh(new THREE.BoxGeometry(0.96, 0.07, 0.60), fmat(0x4d3120, 0.5, 0.0));
+      const apron = new THREE.Mesh(new THREE.BoxGeometry(0.96, 0.07, 0.60), fmat(0x2f1f0e, 0.5, 0.0));
       apron.position.set(0, 0.685, 0); apron.castShadow = true; g.add(apron);
       [[-0.44, -0.28], [0.44, -0.28], [-0.44, 0.28], [0.44, 0.28]].forEach(([lx, lz]) =>
         buildTurnedLeg(g, lx, lz, walnut, 0.72));
       furnitureShadow(g, 0, 0, 1.30, 0.95, 0.6);
     },
     buildChair(g) {
-      const walnut = fmat(0x5a3a24, 0.5, 0.0), damask = fmat(0xe6d8bd, 0.85, 0.0);
+      // Warm WALNUT frame + a tall curved upholstered back that both read clearly from the
+      // default camera (was reading as just a plain cream seat).
+      // Dark walnut base — the bright showroom lifts it to a rich mid-walnut on camera
+      // (a lighter base washes out to blonde oak and loses the "warm walnut" read).
+      const walnut = fmat(0x30200f, 0.45, 0.0), damask = fmat(0xd8c49b, 0.8, 0.0);
       const c = new THREE.Group(); c.position.set(0, 0, 0.70);
-      const seat = new THREE.Mesh(new THREE.BoxGeometry(0.50, 0.11, 0.48), damask);
-      seat.position.set(0, 0.46, 0); seat.castShadow = true; seat.receiveShadow = true; c.add(seat);
-      // Curved, upholstered back — a partial cylinder shell wrapping the sitter's back.
+      // walnut seat rail (frame) with the damask cushion sitting proud on top → walnut shows
+      const rail = new THREE.Mesh(new THREE.BoxGeometry(0.52, 0.09, 0.50), walnut);
+      rail.position.set(0, 0.42, 0); rail.castShadow = true; c.add(rail);
+      const cushion = new THREE.Mesh(new THREE.BoxGeometry(0.46, 0.09, 0.44), damask);
+      cushion.position.set(0, 0.485, 0); cushion.castShadow = true; cushion.receiveShadow = true; c.add(cushion);
+      // Tall curved, upholstered back — a partial cylinder shell wrapping the sitter's back.
       const back = new THREE.Mesh(
-        new THREE.CylinderGeometry(0.30, 0.30, 0.52, 20, 1, true, Math.PI * 0.72, Math.PI * 0.56), damask);
-      back.position.set(0, 0.76, -0.14); back.castShadow = true; c.add(back);
-      // crown rail + turned back posts for the traditional silhouette
-      const crown = new THREE.Mesh(new THREE.CylinderGeometry(0.03, 0.03, 0.44, 12), walnut);
-      crown.rotation.z = Math.PI / 2; crown.position.set(0, 1.02, -0.14); crown.castShadow = true; c.add(crown);
-      [-1, 1].forEach(s => buildTurnedLeg(c, s * 0.20, -0.20, walnut, 0.46));   // front-facing turned legs read fine
-      [-1, 1].forEach(s => buildTurnedLeg(c, s * 0.20, 0.20, walnut, 0.46));
+        new THREE.CylinderGeometry(0.31, 0.31, 0.60, 24, 1, true, Math.PI * 0.72, Math.PI * 0.56), damask);
+      back.position.set(0, 0.84, -0.14); back.castShadow = true; c.add(back);
+      // walnut curved frame hugging the outside of the upholstery + walnut back posts
+      const frame = new THREE.Mesh(
+        new THREE.CylinderGeometry(0.335, 0.335, 0.62, 24, 1, true, Math.PI * 0.70, Math.PI * 0.60), walnut);
+      frame.position.set(0, 0.84, -0.14); frame.castShadow = true; c.add(frame);
+      [-1, 1].forEach(s => {
+        const post = new THREE.Mesh(new THREE.CylinderGeometry(0.026, 0.03, 0.66, 12), walnut);
+        post.position.set(s * 0.235, 0.82, -0.06); post.rotation.x = -0.12; post.castShadow = true; c.add(post);
+      });
+      // crown rail across the top of the back
+      const crown = new THREE.Mesh(new THREE.CylinderGeometry(0.032, 0.032, 0.50, 12), walnut);
+      crown.rotation.z = Math.PI / 2; crown.position.set(0, 1.14, -0.14); crown.castShadow = true; c.add(crown);
+      [-1, 1].forEach(s => buildTurnedLeg(c, s * 0.20, -0.20, walnut, 0.42));   // front-facing turned legs read fine
+      [-1, 1].forEach(s => buildTurnedLeg(c, s * 0.20, 0.20, walnut, 0.42));
       g.add(c);
-      furnitureShadow(g, 0, 0.70, 0.70, 0.70, 0.55);
+      furnitureShadow(g, 0, 0.70, 0.72, 0.72, 0.55);
     },
     buildExtras(g) {
       // A short stack of swatch books + a slim brass candlestick.
@@ -1125,30 +1161,39 @@ const FURNITURE_STYLES = {
   transitional: {
     label: 'Transitional',
     buildTable(g) {
-      const wood = fmat(0x9a7a52, 0.5, 0.0);
+      // WARM honey-oak — clearly a wood tone (not the Contemporary set's cool white).
+      const wood = fmat(0xa9803f, 0.5, 0.0);
       const top = new THREE.Mesh(new THREE.BoxGeometry(1.06, 0.055, 0.70), wood);
       top.position.set(0, 0.73, 0); top.castShadow = true; top.receiveShadow = true; g.add(top);
       // softened edge: a thin inset trim just under the top reads as an eased/beveled edge
-      const trim = new THREE.Mesh(new THREE.BoxGeometry(1.00, 0.03, 0.64), fmat(0x876a46, 0.55, 0.0));
+      const trim = new THREE.Mesh(new THREE.BoxGeometry(1.00, 0.03, 0.64), fmat(0x8a6531, 0.55, 0.0));
       trim.position.set(0, 0.695, 0); trim.castShadow = true; g.add(trim);
       [[-0.45, -0.29], [0.45, -0.29], [-0.45, 0.29], [0.45, 0.29]].forEach(([lx, lz]) =>
         buildTaperedLeg(g, lx, lz, wood, 0.70));
       furnitureShadow(g, 0, 0, 1.30, 0.95, 0.6);
     },
     buildChair(g) {
-      const wood = fmat(0x9a7a52, 0.55, 0.0), greige = fmat(0xd8cfc0, 0.88, 0.0);
+      // Soft, rounded upholstered chair — a curved barrel back + bolstered front, so its
+      // silhouette reads clearly SOFTER than the Contemporary set's crisp rectilinear chair.
+      const wood = fmat(0xa9803f, 0.55, 0.0), greige = fmat(0xcfc3af, 0.9, 0.0);
       const c = new THREE.Group(); c.position.set(0, 0, 0.69);
-      const seat = new THREE.Mesh(new THREE.BoxGeometry(0.49, 0.10, 0.47), greige);
+      const seat = new THREE.Mesh(new THREE.BoxGeometry(0.50, 0.12, 0.48), greige);
       seat.position.set(0, 0.45, 0); seat.castShadow = true; seat.receiveShadow = true; c.add(seat);
-      const back = new THREE.Mesh(new THREE.BoxGeometry(0.49, 0.42, 0.06), greige);
-      back.position.set(0, 0.68, -0.20); back.castShadow = true; c.add(back);
-      // softened top rail — a rounded cap along the backrest top
-      const cap = new THREE.Mesh(new THREE.CylinderGeometry(0.035, 0.035, 0.49, 14), wood);
-      cap.rotation.z = Math.PI / 2; cap.position.set(0, 0.89, -0.20); cap.castShadow = true; c.add(cap);
+      // rounded front bolster cushion so the seat edge reads plump, not sharp
+      const bolster = new THREE.Mesh(new THREE.CylinderGeometry(0.075, 0.075, 0.50, 20), greige);
+      bolster.rotation.z = Math.PI / 2; bolster.position.set(0, 0.45, 0.23); bolster.castShadow = true; c.add(bolster);
+      // curved upholstered barrel back — a gently wrapping cylinder shell (soft, not a flat panel)
+      const back = new THREE.Mesh(
+        new THREE.CylinderGeometry(0.33, 0.33, 0.46, 24, 1, true, Math.PI * 0.76, Math.PI * 0.48), greige);
+      back.position.set(0, 0.70, -0.16); back.castShadow = true; c.add(back);
+      // fill the barrel-back shell so it reads solid/plush from behind too
+      const backCore = new THREE.Mesh(
+        new THREE.CylinderGeometry(0.30, 0.30, 0.44, 20, 1, true, Math.PI * 0.78, Math.PI * 0.44), greige);
+      backCore.position.set(0, 0.70, -0.16); c.add(backCore);
       [[-0.20, -0.20], [0.20, -0.20], [-0.20, 0.20], [0.20, 0.20]].forEach(([lx, lz]) =>
         buildTaperedLeg(c, lx, lz, wood, 0.45));
       g.add(c);
-      furnitureShadow(g, 0, 0.69, 0.68, 0.68, 0.55);
+      furnitureShadow(g, 0, 0.69, 0.70, 0.70, 0.55);
     },
     buildExtras(g) {
       // A fan of swatch cards + a compact potted plant — a working-consultation blend.
@@ -1169,20 +1214,28 @@ const FURNITURE_STYLES = {
   brutalist: {
     label: 'Brutalist',
     buildTable(g) {
-      const slab = fmat(0x8f8d88, 0.95, 0.0), mass = fmat(0x807e79, 0.96, 0.0);
+      // RAW CONCRETE (béton brut): warm medium grey slab + a darker poured plinth for tonal
+      // mass, high roughness, zero metalness — reads as concrete, never white foam.
+      // Bases run dark because the showroom lighting lifts everything ~1.6x — these READ as
+      // a warm medium concrete grey (~#8a8a86) on camera, not white foam.
+      const slab = fmat(0x605f5b, 0.9, 0.0), mass = fmat(0x4e4d49, 0.92, 0.0);
+      const groove = fmat(0x353430, 0.95, 0.0);
       const top = new THREE.Mesh(new THREE.BoxGeometry(1.18, 0.14, 0.78), slab);
       top.position.set(0, 0.70, 0); top.castShadow = true; top.receiveShadow = true; g.add(top);
       // one monolithic plinth base — a single solid block, not legs
       const base = new THREE.Mesh(new THREE.BoxGeometry(0.52, 0.63, 0.52), mass);
       base.position.set(0, 0.315, 0); base.castShadow = true; base.receiveShadow = true; g.add(base);
+      buildBoardFormLines(g, 0, 0.315, 0, 0.52, 0.63, 0.52, groove, 3);
       furnitureShadow(g, 0, 0, 1.35, 1.0, 0.68);
     },
     buildChair(g) {
-      const seatMat = fmat(0x7f7d78, 0.95, 0.0), backMat = fmat(0x6f6d68, 0.96, 0.0);
+      const seatMat = fmat(0x605f5b, 0.9, 0.0), backMat = fmat(0x4e4d49, 0.92, 0.0);
+      const groove = fmat(0x353430, 0.95, 0.0);
       const c = new THREE.Group(); c.position.set(0, 0, 0.72);
       // blocky monolithic seat plinth straight to the floor (no legs)
       const seat = new THREE.Mesh(new THREE.BoxGeometry(0.52, 0.44, 0.52), seatMat);
       seat.position.set(0, 0.22, 0); seat.castShadow = true; seat.receiveShadow = true; c.add(seat);
+      buildBoardFormLines(c, 0, 0.22, 0, 0.52, 0.44, 0.52, groove, 3);
       // a thick angular slab back
       const back = new THREE.Mesh(new THREE.BoxGeometry(0.52, 0.50, 0.14), backMat);
       back.position.set(0, 0.67, -0.19); back.castShadow = true; c.add(back);
@@ -1191,9 +1244,9 @@ const FURNITURE_STYLES = {
     },
     buildExtras(g) {
       // A heavy concrete monolith object on the slab — a brutalist paperweight.
-      const monolith = new THREE.Mesh(new THREE.BoxGeometry(0.16, 0.30, 0.16), fmat(0x9a9892, 0.95, 0.0));
+      const monolith = new THREE.Mesh(new THREE.BoxGeometry(0.16, 0.30, 0.16), fmat(0x605f5b, 0.9, 0.0));
       monolith.position.set(-0.24, 0.77 + 0.15, 0.04); monolith.rotation.y = 0.4; monolith.castShadow = true; g.add(monolith);
-      const cube = new THREE.Mesh(new THREE.BoxGeometry(0.14, 0.14, 0.14), fmat(0x76746f, 0.96, 0.0));
+      const cube = new THREE.Mesh(new THREE.BoxGeometry(0.14, 0.14, 0.14), fmat(0x47463f, 0.92, 0.0));
       cube.position.set(0.26, 0.77 + 0.07, -0.02); cube.rotation.y = 0.7; cube.castShadow = true; g.add(cube);
     }
   }

← 20c04ec chore: v1.3.1 (session close — thumb/proxy upstream hardenin  ·  back to Quadrille Showroom  ·  furniture: art-direction fixes — concrete-grey Brutalist, wa 0a7d7ab →