← back to Model Arena
mood-board-studio: use real DW products + photos instead of synthetic SVG swatches
d3bffed6e76ac9a6e146532256d3ec886b212cdc · 2026-07-30 15:19:04 -0700 · Steve
- Swatch tiles now render real ACTIVE catalog products (dw_unified mirror):
live cdn.shopify.com photo + real name/vendor/SKU/price, 8 per material,
rotating so each spawn reveals a different SKU; dbl-click opens the product.
- Procedural SVG textures kept as an onerror fallback (never a broken image).
- PNG export draws the real photos (CORS-clean via crossorigin=anonymous +
Shopify ACAO:*) so exported boards are untainted; split into
drawSwatchOnCanvas/drawProceduralSwatch/drawSwatchLabel.
- server.js: widen ONLY the /game route CSP img-src to allow cdn.shopify.com
(artifact route stays locked to data:/blob:).
Verified in-browser: 6/6 photos load, 0 console errors, canvas export clean.
Files touched
M public/games/dw-mood-board-studio.htmlM server.js
Diff
commit d3bffed6e76ac9a6e146532256d3ec886b212cdc
Author: Steve <steve@designerwallcoverings.com>
Date: Thu Jul 30 15:19:04 2026 -0700
mood-board-studio: use real DW products + photos instead of synthetic SVG swatches
- Swatch tiles now render real ACTIVE catalog products (dw_unified mirror):
live cdn.shopify.com photo + real name/vendor/SKU/price, 8 per material,
rotating so each spawn reveals a different SKU; dbl-click opens the product.
- Procedural SVG textures kept as an onerror fallback (never a broken image).
- PNG export draws the real photos (CORS-clean via crossorigin=anonymous +
Shopify ACAO:*) so exported boards are untainted; split into
drawSwatchOnCanvas/drawProceduralSwatch/drawSwatchLabel.
- server.js: widen ONLY the /game route CSP img-src to allow cdn.shopify.com
(artifact route stays locked to data:/blob:).
Verified in-browser: 6/6 photos load, 0 console errors, canvas export clean.
---
public/games/dw-mood-board-studio.html | 144 +++++++++++++++++++++++++++++----
server.js | 3 +-
2 files changed, 132 insertions(+), 15 deletions(-)
diff --git a/public/games/dw-mood-board-studio.html b/public/games/dw-mood-board-studio.html
index 538ccf8..9e92c42 100644
--- a/public/games/dw-mood-board-studio.html
+++ b/public/games/dw-mood-board-studio.html
@@ -408,15 +408,40 @@ header {
}
.swatch-tile .tile-label {
- background: rgba(0,0,0,0.45);
- color: rgba(255,255,255,0.90);
+ background: rgba(0,0,0,0.52);
+ color: rgba(255,255,255,0.92);
backdrop-filter: blur(4px);
+ display: flex;
+ flex-direction: column;
+ gap: 1px;
+ line-height: 1.18;
+}
+.swatch-tile .tl-name {
+ font-weight: 600;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+.swatch-tile .tl-vendor {
+ font-size: 8px;
+ letter-spacing: .02em;
+ opacity: 0.72;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
}
.swatch-svg {
width: 100%;
height: 100%;
}
+.swatch-img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ display: block;
+ background: #1a1712;
+}
/* ============================================================
COLOR CHIP TILES
@@ -877,6 +902,31 @@ header {
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script>
+/* ============================================================
+ REAL DW PRODUCTS (live catalog — dw_unified mirror, status=ACTIVE,
+ real cdn.shopify.com photos). Keyed by material type; each product:
+ { name, vendor, sku, handle, img, price }
+ ============================================================ */
+const PRODUCTS = {"chinoiserie":[{"name":"Jardin Fleuri - Ivory Multi By Brunschwig & Fils | Summer Palace Wallcoverings |Chinoiserie Botanical & Floral Wallcovering Print","vendor":"Brunschwig & Fils","sku":"DWKK-102285","handle":"dwkk-102285","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/P8019118_192_950782b7-bc9c-4b7a-a0a9-49fae23ee1a0.jpg?v=1725922617","price":1567},{"name":"La Pagode - Teal By Brunschwig & Fils | Les Ensembliers Wallcoverings | Chinoiserie Wallcovering Embroidery","vendor":"Brunschwig & Fils","sku":"DWKK-102331","handle":"dwkk-102331","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/P8020108_35_f3afd4ba-dbe6-4ac9-a195-b930f188a780.jpg?v=1725922727","price":393},{"name":"Tongli - Fern Multi By Brunschwig & Fils | Summer Palace Wallcoverings |Chinoiserie Botanical & Floral Wallcovering Print","vendor":"Brunschwig & Fils","sku":"DWKK-102274","handle":"dwkk-102274","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/P8019115_33_a7361bcf-88e8-40df-b0fa-23c228b55301.jpg?v=1725922594","price":1394},{"name":"Ming Dragon Print - Claret Multi By Brunschwig & Fils | Summer Palace |Chinoiserie Novelty Multipurpose Print","vendor":"Brunschwig & Fils","sku":"DWKK-100756","handle":"dwkk-gc1f32da2","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/8019140_195_1fb0f24d-239b-41e5-a453-692799b39101.jpg?v=1725919361","price":298},{"name":"Jardin Fleuri - Delft Multi By Brunschwig & Fils | Summer Palace Wallcoverings |Chinoiserie Botanical & Floral Wallcovering Print","vendor":"Brunschwig & Fils","sku":"DWKK-102282","handle":"dwkk-102282","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/P8019118_153_b7b10748-93ce-4c18-9649-24aa6370d0cc.jpg?v=1725922610","price":1567},{"name":"Ming Dragon - Indigo Multi By Brunschwig & Fils | Summer Palace Wallcoverings | Chinoiserie Wallcovering Print","vendor":"Brunschwig & Fils","sku":"DWKK-102269","handle":"dwkk-102269","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/P8019113_50_c18abe3c-8f2d-4eda-9618-afb98f493602.jpg?v=1725922585","price":605},{"name":"Tongli - Lagoon Turquoise By Brunschwig & Fils | Summer Palace Wallcoverings |Chinoiserie Botanical & Floral Wallcovering Print","vendor":"Brunschwig & Fils","sku":"DWKK-102272","handle":"dwkk-102272","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/P8019115_133_9415dc52-c6cd-4caf-8098-030e271046f5.jpg?v=1725922590","price":1394},{"name":"Jardin Fleuri - Opal Multi By Brunschwig & Fils | Summer Palace Wallcoverings |Chinoiserie Botanical & Floral Wallcovering Print","vendor":"Brunschwig & Fils","sku":"DWKK-102283","handle":"dwkk-102283","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/P8019118_157_3d36ee49-50bf-4eb9-9487-f3ad353c0648.jpg?v=1725922612","price":1567}],"damask":[{"name":"Seville Weave - Navy/Marine Blue By Lee Jofa | Triana Weaves |Damask Texture Upholstery","vendor":"Lee Jofa","sku":"DWKK-135512","handle":"dwkk-gb1fadc09","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/2021108_155_edbb8cca-1e09-466e-9d25-7bbb048f1766.jpg?v=1726530135","price":312},{"name":"Acanthus Damask - Blue Blue By Lee Jofa | Oscar De La Renta Iv | Damask Multipurpose","vendor":"Lee Jofa","sku":"DWKK-135434","handle":"dwkk-g7c9cb323","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/2020212_1516_93e2821c-87c7-4b44-93e5-cb517b7c85bf.jpg?v=1726529925","price":298},{"name":"Granada Print - Ruby Red By Lee Jofa | Oscar De La Renta Iv | Damask Multipurpose Print","vendor":"Lee Jofa","sku":"DWKK-135458","handle":"dwkk-g989f77c9","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/2020220_924_1bb4d925-b513-4eba-9892-2c06fa1e3047.jpg?v=1726529992","price":376},{"name":"Damask Pierre - Spice Orange By Brunschwig & Fils | B&F Showroom Exclusive 2019 | Damask Upholstery Silk","vendor":"Brunschwig & Fils","sku":"DWKK-100206","handle":"dwkk-gbf4f8dda","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/8013188_440_ea0b5f00-53dd-4f6b-b737-e39f0b24f6f2.jpg?v=1725918203","price":627},{"name":"Hayes Embroidery - Ivory Ivory By Lee Jofa | Bunny Williams Arcadia | Damask Drapery Embroidery","vendor":"Lee Jofa","sku":"DWKK-135611","handle":"dwkk-gbdde20cf","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/2022110_1_1f27128b-18c2-40b9-b7a0-db1b5cf4decc.jpg?v=1726530384","price":355},{"name":"Damask Pierre - Eggplant Plum By Brunschwig & Fils | B&F Showroom Exclusive 2019 | Damask Upholstery Silk","vendor":"Brunschwig & Fils","sku":"DWKK-100214","handle":"dwkk-g2fda5c11","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/8013188_909_94ed9012-9887-4107-9e65-82d2027ee3cc.jpg?v=1725918220","price":627},{"name":"Arnaud Damask - Aqua Light Blue By Brunschwig & Fils | Vienne Silks | Damask Multipurpose Silk","vendor":"Brunschwig & Fils","sku":"DWKK-101383","handle":"dwkk-gff434372","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/8023150_13_071e61ae-d6c8-4125-b482-8612dfcae110.jpg?v=1725920760","price":327},{"name":"Gambetta Weave - Rose Coral By Brunschwig & Fils | Alsace Weaves | Damask Upholstery","vendor":"Brunschwig & Fils","sku":"DWKK-100702","handle":"dwkk-g2d18508a","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/8019120_197_676b1bc3-4715-4799-848c-48b978414aff.jpg?v=1725919242","price":208}],"grasscloth":[{"name":"Grasscloth Slubs Natura | Phillipe Romano","vendor":"Phillipe Romano","sku":"GRS-1811","handle":"grasscloth-slubs-natura-grs-1811","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/products/a731e8e333ad16ef3b05543b830e00de_7ec623bc-ba2d-44dd-8884-176af8ed4f39.jpg?v=1745460826","price":23},{"name":"Hula - Black Black By Lee Jofa Modern | Hunt Slonem Ii | Modern Wallcovering Grasscloth","vendor":"Groundworks","sku":"DWKK-139617","handle":"dwkk-139617","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/GWP-3713_84_e688a483-e43f-4d03-b8a3-d9a6741c8a75.jpg?v=1753291483","price":444},{"name":"Diamond Grasscloth Tribal | Phillipe Romano","vendor":"Phillipe Romano","sku":"GRS-1804","handle":"diamond-grasscloth-tribal-grs-1804","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/products/9014b0c5b6e28bcffc717479cca2f5e0.jpg?v=1745460833","price":23},{"name":"Plantation Grasscloth | Phillipe Romano","vendor":"Phillipe Romano","sku":"GRS-1806","handle":"plantation-grasscloth-grs-1806","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/plantation-grasscloth-grs-1806-cropped.jpg?v=1774341392","price":23},{"name":"Fine Raffia - Natural Beige By Lee Jofa Modern | Elements Ii Naturals |Solid Texture Wallcovering Grasscloth","vendor":"Groundworks","sku":"DWKK-139575","handle":"dwkk-g328a6d2a","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/GWP-3415_16_ae628f14-0e94-479b-bfd8-a11a13484a57.jpg?v=1753291589","price":138},{"name":"Pali Basketweave Paperweave Grasscloth | Phillipe Romano","vendor":"Phillipe Romano","sku":"GRA-39052","handle":"pali-basketweave-paperweave-grasscloth-gra-39052","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/products/da6ddb3316744cc200ef1ea31be33850.jpg?v=1745459005","price":5},{"name":"Hank's Horizontal Grasscloth | Phillipe Romano","vendor":"Phillipe Romano","sku":"GRS-38023","handle":"hank-s-horizontal-grasscloth-wall-paper-grs-38023","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/grs-1495926177904-cropped_5f436f24-2e50-4830-ac9a-898a00c493f6.jpg?v=1774344984","price":4},{"name":"Exhale Woven Faux Grasscloth - Moss Residential Wallcovering | Jeffrey Stevens","vendor":"Jeffrey Stevens","sku":"DWJS-91046","handle":"exhale-woven-faux-grasscloth-moss-residential-wallpaper-jeffrey-stevens","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/0093554_exhale-moss-woven-faux-grasscloth-wallpaper.jpg?v=1775163868","price":null}],"linen":[{"name":"Watermill Linen - Onyx Black By Lee Jofa | Colour Complements Ii |Solid Texture Multipurpose","vendor":"Lee Jofa","sku":"DWKK-134386","handle":"dwkk-gdd16bc69","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/2012176_8_e25de644-ce6f-4603-a697-1e5f36bb9eae.jpg?v=1726547195","price":165},{"name":"Dublin Linen - Slate Grey By Lee Jofa | Colour Complements Ii |Solid Texture Multipurpose","vendor":"Lee Jofa","sku":"DWKK-134356","handle":"dwkk-g17f20967","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/2012175_52_80c9d662-5da8-4ff1-9447-3bac642a912d.jpg?v=1726547112","price":63},{"name":"Watermill Linen - Cream White By Lee Jofa | Colour Complements Ii |Solid Texture Multipurpose","vendor":"Lee Jofa","sku":"DWKK-134362","handle":"dwkk-ge8a4d5f8","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/2012176_1_bc47c44b-f4c6-4839-b5db-44147e1a00d7.jpg?v=1726547127","price":165},{"name":"Bankers Linen - Red Red By Brunschwig & Fils | Gis |Solid Texture Multipurpose Linen","vendor":"Brunschwig & Fils","sku":"DWKK-100605","handle":"dwkk-ge6b86835","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/8017144_19_41d7b025-6adc-461b-939d-df24fcd879fc.jpg?v=1725919035","price":150},{"name":"Bankers Linen - Cream White By Brunschwig & Fils | Le Jardin Chinois |Solid Texture Multipurpose Linen","vendor":"Brunschwig & Fils","sku":"DWKK-100024","handle":"dwkk-g86428c9f","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/8012140_111_d5520c5c-231a-47b0-b595-e5b7f49dd89c.jpg?v=1725917826","price":150},{"name":"Bankers Linen - Ink Blue By Brunschwig & Fils | Le Jardin Chinois |Solid Texture Multipurpose Linen","vendor":"Brunschwig & Fils","sku":"DWKK-100038","handle":"dwkk-gd3d7f11a","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/8012140_50_463fe0c2-c2f6-46f9-8e36-b67c267b4b07.jpg?v=1725917855","price":150},{"name":"Dublin Linen - Prism Grey By Lee Jofa | Colour Library Vii |Solid Texture Multipurpose","vendor":"Lee Jofa","sku":"DWKK-134328","handle":"dwkk-g06da8410","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/2012175_1101_28895aed-245e-44cd-9ef9-f5c5e388791e.jpg?v=1726547029","price":63},{"name":"Lille Linen - Midgnight Dark Blue By Lee Jofa | Gis | Solid Multipurpose","vendor":"Lee Jofa","sku":"DWKK-134752","handle":"dwkk-gf234f2cd","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/2017119_50_a4368812-7556-47ba-a7b1-0e10ba5a1b92.jpg?v=1726548252","price":150}],"raffia":[{"name":"Fine Raffia - Natural Beige By Lee Jofa Modern | Elements Ii Naturals |Solid Texture Wallcovering Grasscloth","vendor":"Groundworks","sku":"DWKK-139575","handle":"dwkk-g328a6d2a","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/GWP-3415_16_ae628f14-0e94-479b-bfd8-a11a13484a57.jpg?v=1753291589","price":138},{"name":"Cabo Raffia Wallcovering | Thibaut","vendor":"Thibaut","sku":"DWTT-80391","handle":"dwtt-80391-designer-wallcoverings-los-angeles","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/T41160_d90c5f1b-3086-4237-851f-332ec133f1c8.jpg?v=1781202567","price":196},{"name":"Bethany Raffia Off White - Off White Wallcovering | Thibaut","vendor":"Thibaut","sku":"DWTT-80363","handle":"dwtt-80363-designer-wallcoverings-los-angeles","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/T19602_05477fe1-50d1-4fa8-b5ec-f23c50e31727.jpg?v=1781202597","price":379},{"name":"Sea Bright Plain Paper Weave Raffia Wallcovering | Phillipe Romano","vendor":"Phillipe Romano","sku":"PWV-500950","handle":"sea-bright-plain-paper-weave-raffia-paper-weave-wallcovering-phillipe-romano","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/baf2884af6422e26083a1b2f6e07e3cd.jpg?v=1783880263","price":null},{"name":"Raffia Decor - Smoke | Scalamandre","vendor":"Scalamandre Wallpaper","sku":"DWA-88550","handle":"wtt661468","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/products/WTT661468.jpg?v=1745346442","price":null},{"name":"Bankun Raffia Antique - Antique Natural Wallcovering | Thibaut","vendor":"Thibaut","sku":"DWTT-72328","handle":"dwtt-72328-designer-wallcoverings-los-angeles","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/T6817_89424133-37e8-4acd-9f01-2b49bbf46f15.jpg?v=1781202941","price":4},{"name":"Madagascar Cloth 3 Raffia","vendor":"Designer Wallcoverings","sku":"DWC-278566","handle":"dwc-278566-designerwallcoverings-com","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/DWC-278566-DesignerWallcoverings-Los-Angeles-1_a3bede8c-b94d-477d-b0c2-97ad23dba752.png?v=1741881338","price":null},{"name":"Taiki Paper Weave Raffia | Phillipe Romano","vendor":"Phillipe Romano","sku":"XQA-33723","handle":"taiki-paper-weave-raffia-xqa-33723","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/products/69007e51570bb9c98f02694ac9a82280_13600e9f-b3b4-4f31-9801-14c9e2a4965a.jpg?v=1745457371","price":4}],"silk":[{"name":"Damask Pierre - Chestnut Brown By Brunschwig & Fils | B&F Showroom Exclusive 2019 | Damask Upholstery Silk","vendor":"Brunschwig & Fils","sku":"DWKK-100209","handle":"dwkk-g761bb5a0","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/8013188_606_2cb44e0a-37d7-4b04-8e4b-96307660072f.jpg?v=1725918209","price":627},{"name":"Damask Pierre - Red Red By Brunschwig & Fils | B&F Showroom Exclusive 2019 | Damask Upholstery Silk","vendor":"Brunschwig & Fils","sku":"DWKK-100213","handle":"dwkk-gd8647d7a","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/8013188_9_61cc5b9b-d2be-4a47-8c79-577e47820436.jpg?v=1725918217","price":627},{"name":"Arnaud Damask - Aqua Light Blue By Brunschwig & Fils | Vienne Silks | Damask Multipurpose Silk","vendor":"Brunschwig & Fils","sku":"DWKK-101383","handle":"dwkk-gff434372","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/8023150_13_071e61ae-d6c8-4125-b482-8612dfcae110.jpg?v=1725920760","price":327},{"name":"Damask Pierre - Sand Beige By Brunschwig & Fils | B&F Showroom Exclusive 2019 | Damask Upholstery Silk","vendor":"Brunschwig & Fils","sku":"DWKK-100198","handle":"dwkk-g55f0dcc6","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/8013188_1116_af6caecd-9ea2-4855-a05d-e55f7ad2465d.jpg?v=1725918185","price":627},{"name":"Damask Pierre - Grey Grey By Brunschwig & Fils | B&F Showroom Exclusive 2019 | Damask Upholstery Silk","vendor":"Brunschwig & Fils","sku":"DWKK-100195","handle":"dwkk-g28f6f736","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/8013188_11_07b058dc-7c5d-4008-bb9b-fce99728b8c9.jpg?v=1725918179","price":627},{"name":"Damask Pierre - Delft Blue By Brunschwig & Fils | B&F Showroom Exclusive 2019 | Damask Upholstery Silk","vendor":"Brunschwig & Fils","sku":"DWKK-100207","handle":"dwkk-ga7c82fa0","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/8013188_50_8afa77cd-9bed-4841-aa5f-a05040e880ff.jpg?v=1725918205","price":627},{"name":"Damask Pierre - Olive Green By Brunschwig & Fils | B&F Showroom Exclusive 2019 | Damask Upholstery Silk","vendor":"Brunschwig & Fils","sku":"DWKK-100202","handle":"dwkk-ged4f176b","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/8013188_30_75ebeb88-3494-4242-a679-efffd52a9959.jpg?v=1725918194","price":627},{"name":"Damask Pierre - Snow White By Brunschwig & Fils | B&F Showroom Exclusive 2019 | Damask Upholstery Silk","vendor":"Brunschwig & Fils","sku":"DWKK-100193","handle":"dwkk-g48d398e0","img":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/8013188_1_00da697d-d623-4bc3-90d4-02a0d59aaff8.jpg?v=1725918174","price":627}]};
+
+/* Rotating picker — each spawn surfaces the next real product for its material,
+ so switching / reshuffling presets keeps revealing different live SKUs. */
+const _prodIdx = {};
+function pickProduct(type) {
+ const pool = PRODUCTS[type] || [];
+ if (!pool.length) return null;
+ const i = (_prodIdx[type] || 0) % pool.length;
+ _prodIdx[type] = i + 1;
+ return pool[i];
+}
+/* "Jardin Fleuri - Ivory Multi By Brunschwig ... | Collection | Type" -> "Jardin Fleuri - Ivory Multi" */
+function productShortName(name) {
+ return (name || '').split(/\s+By\s+|\s*\|\s*/)[0].replace(/\s+/g, ' ').trim();
+}
+function productHref(handle) {
+ return 'https://www.designerwallcoverings.com/products/' + handle;
+}
+
/* ============================================================
STYLE PRESET DATA
============================================================ */
@@ -1312,8 +1362,38 @@ function createSwatchTile(type, palette, x, y, z, id) {
el.style.top = y + 'px';
el.style.zIndex = z;
- const svgStr = buildSwatchSVG(type, palette);
- el.innerHTML = `${svgStr}<div class="tile-label">${SWATCH_LABELS[type] || type}</div>`;
+ const prod = pickProduct(type);
+ const svgStr = buildSwatchSVG(type, palette); // procedural fallback texture
+
+ if (!prod) {
+ el.innerHTML = `${svgStr}<div class="tile-label">${SWATCH_LABELS[type] || type}</div>`;
+ return el;
+ }
+
+ // Real DW product photo
+ el.dataset.material = type;
+ el.dataset.product = JSON.stringify(prod);
+ const short = productShortName(prod.name);
+ const priceStr = (typeof prod.price === 'number' && prod.price > 0) ? ' · $' + prod.price : '';
+ const esc = s => String(s || '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
+ el.title = `${prod.name} — ${prod.vendor}${prod.sku ? ' · ' + prod.sku : ''}${priceStr}\n(double-click to open on designerwallcoverings.com)`;
+
+ el.innerHTML =
+ `<img class="swatch-img" crossorigin="anonymous" loading="lazy" alt="${esc(short)}" src="${esc(prod.img)}">` +
+ `<div class="tile-label">` +
+ `<span class="tl-name">${esc(short)}</span>` +
+ `<span class="tl-vendor">${esc(prod.vendor)}${esc(priceStr)}</span>` +
+ `</div>`;
+
+ // Degrade gracefully to the procedural texture if a photo ever fails to load
+ const img = el.querySelector('.swatch-img');
+ img.addEventListener('error', () => {
+ el.classList.add('img-failed');
+ el.innerHTML = `${svgStr}<div class="tile-label">${SWATCH_LABELS[type] || type}</div>`;
+ }, { once: true });
+ // Double-click opens the live product (single click stays reserved for drag/select)
+ el.addEventListener('dblclick', () => window.open(productHref(prod.handle), '_blank', 'noopener'));
+
return el;
}
@@ -1875,7 +1955,52 @@ async function openExport() {
/* --- Sub-renderers for canvas export --- */
function drawSwatchOnCanvas(ctx, t, x, y, w, h, palette) {
- const swatchType = t.el.querySelector('.tile-label')?.textContent?.toLowerCase().replace(/\s+/g,'') || '';
+ // Prefer the REAL product photo. Images load with crossorigin="anonymous" and
+ // Shopify CDN sends Access-Control-Allow-Origin:* — so drawing them does NOT
+ // taint the export canvas and toBlob() still works.
+ const imgEl = t.el.querySelector('.swatch-img');
+ if (imgEl && imgEl.complete && imgEl.naturalWidth > 0) {
+ const ir = imgEl.naturalWidth / imgEl.naturalHeight;
+ const dr = w / h;
+ let sx, sy, sw, sh; // object-fit: cover source rect
+ if (ir > dr) { sh = imgEl.naturalHeight; sw = sh * dr; sx = (imgEl.naturalWidth - sw) / 2; sy = 0; }
+ else { sw = imgEl.naturalWidth; sh = sw / dr; sx = 0; sy = (imgEl.naturalHeight - sh) / 2; }
+ try {
+ ctx.drawImage(imgEl, sx, sy, sw, sh, x, y, w, h);
+ } catch (e) {
+ drawProceduralSwatch(ctx, t, x, y, w, h, palette);
+ }
+ drawSwatchLabel(ctx, t, x, y, w, h);
+ return;
+ }
+ drawProceduralSwatch(ctx, t, x, y, w, h, palette);
+ drawSwatchLabel(ctx, t, x, y, w, h);
+}
+
+function drawSwatchLabel(ctx, t, x, y, w, h) {
+ let label = '';
+ const pj = t.el.dataset.product;
+ if (pj) { try { label = productShortName(JSON.parse(pj).name); } catch (e) {} }
+ if (!label) label = t.el.querySelector('.tl-name')?.textContent
+ || t.el.querySelector('.tile-label')?.textContent || '';
+ ctx.fillStyle = 'rgba(0,0,0,0.52)';
+ ctx.fillRect(x, y + h - h * 0.18, w, h * 0.18);
+ ctx.font = `700 ${Math.round(w * 0.062)}px -apple-system,Helvetica,Arial,sans-serif`;
+ ctx.fillStyle = 'rgba(255,255,255,0.92)';
+ ctx.textAlign = 'left';
+ const maxW = w * 0.88;
+ let txt = label.toUpperCase();
+ if (ctx.measureText(txt).width > maxW) {
+ while (txt.length > 4 && ctx.measureText(txt + '…').width > maxW) txt = txt.slice(0, -1);
+ txt = txt.replace(/[\s\-]+$/, '') + '…';
+ }
+ ctx.fillText(txt, x + w * 0.06, y + h - h * 0.06);
+}
+
+function drawProceduralSwatch(ctx, t, x, y, w, h, palette) {
+ const swatchType = (t.el.dataset.material
+ || t.el.querySelector('.tile-label')?.textContent
+ || '').toLowerCase().replace(/\s+/g,'');
if (swatchType.includes('grasscloth')) {
const bg = palette[4]?.hex || '#d4c4a4';
@@ -1971,15 +2096,6 @@ function drawSwatchOnCanvas(ctx, t, x, y, w, h, palette) {
ctx.fill();
ctx.globalAlpha = 1;
}
-
- // Label strip
- ctx.fillStyle = 'rgba(0,0,0,0.40)';
- ctx.fillRect(x, y + h - h*0.16, w, h*0.16);
- ctx.font = `700 ${Math.round(w * 0.07)}px -apple-system,Helvetica,Arial,sans-serif`;
- ctx.fillStyle = 'rgba(255,255,255,0.85)';
- ctx.textAlign = 'left';
- const labelText = t.el.querySelector('.tile-label')?.textContent || '';
- ctx.fillText(labelText.toUpperCase(), x + w*0.06, y + h - h*0.04);
}
function drawChipOnCanvas(ctx, el, x, y, w, h) {
diff --git a/server.js b/server.js
index eacfb60..9495984 100644
--- a/server.js
+++ b/server.js
@@ -936,7 +936,8 @@ iframe{width:100%;height:460px;border:0;background:#000}.c.win iframe{height:560
if (!fs.existsSync(fp)) { res.writeHead(404); return res.end('no game'); }
res.writeHead(200, {
'Content-Type': 'text/html; charset=utf-8',
- 'Content-Security-Policy': "default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; img-src data: blob:; media-src data: blob:; font-src data:",
+ // games may pull real DW product photos from the Shopify CDN (img-src)
+ 'Content-Security-Policy': "default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; img-src data: blob: https://cdn.shopify.com; media-src data: blob:; font-src data:",
'X-Frame-Options': 'SAMEORIGIN',
});
return res.end(fs.readFileSync(fp));
← 2aa4931 auto-save: 2026-07-30T07:45:45 (6 files) — data/challenges.j
·
back to Model Arena
·
mood-board-studio: real products only — drop color chips, ty a236fbf →