← back to Wallpaper Paint Antics

series2/episodes/06-built-for-busy-spaces.html

215 lines

<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=1080,height=1920"><title>Wallo presents — Performance in busy spaces</title><style>@font-face{font-family:Georgia;src:url('../assets/georgia.ttf')}@font-face{font-family:Verdana;src:url('../assets/verdana.ttf')}html,body{margin:0;width:1080px;height:1920px;overflow:hidden}#root{position:relative;width:1080px;height:1920px;overflow:hidden}.clip{position:absolute;inset:0;width:1080px;height:1920px}canvas{width:1080px;height:1920px}.preload{position:absolute;opacity:0;pointer-events:none}</style><script src="../assets/gsap.min.js"></script></head><body><div id="root" data-composition-id="main" data-start="0" data-duration="20" data-width="1080" data-height="1920" data-fps="30"><section id="material-scene" class="clip" data-start="0" data-duration="20" data-track-index="0"><canvas id="stage" width="1080" height="1920" aria-label="Wallo presenting Performance in busy spaces"></canvas><span class="preload" style="font-family:Georgia">Wallo</span><span class="preload" style="font-family:Verdana">Designer Wallcoverings</span></section><audio id="narrated-mix" src="../assets/mix-2.wav" data-start="0" data-duration="20" data-track-index="1"></audio></div><script>window.EPISODE={"slug":"06-built-for-busy-spaces","title":"Beautiful.\nBuilt for daily life.","shortTitle":"Performance in busy spaces","kicker":"03 / MATERIAL PERFORMANCE","product":"wheaton","note":"Type II performance varies by product. Request specifications.","takeaway":"Specify for the way a room lives.","lines":["In a busy space, the finish has a job to do.","Type two vinyl offers practical durability for demanding interiors.","Wheaton pairs a woven look with a commercial vinyl construction.","Check abrasion, cleanability, and the manufacturer's care instructions before specifying."],"captions":["Start with the room's daily demands.","Consider Type II commercial vinyl.","Wheaton: a woven look in Type II vinyl.","Check abrasion. Cleanability. Care."],"index":2,"audio":{"slug":"06-built-for-busy-spaces","provider":"Kokoro local","voice":"am_michael","duration":20,"segments":[{"index":0,"start":0.6,"end":3.693,"text":"In a busy space, the finish has a job to do.","caption":"Start with the room's daily demands.","duration":3.093},{"index":1,"start":5.1,"end":9.334,"text":"Type two vinyl offers practical durability for demanding interiors.","caption":"Consider Type II commercial vinyl.","duration":4.234},{"index":2,"start":9.6,"end":13.675,"text":"Wheaton pairs a woven look with a commercial vinyl construction.","caption":"Wheaton: a woven look in Type II vinyl.","duration":4.075},{"index":3,"start":14.1,"end":18.902,"text":"Check abrasion, cleanability, and the manufacturer's care instructions before specifying.","caption":"Check abrasion. Cleanability. Care.","duration":4.802}],"energy":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0012,0.0015,0.0021,0.2127,0.6512,0.6471,0.7932,0.945,0.9537,0.727,0.4854,0.3062,0.2264,0.1563,0.1451,1,1,1,1,1,0.3182,0.2795,0.3123,0.4062,0.506,0.3977,0.3576,0.3326,0.5822,0.6731,0.5581,0.5063,0.1297,0.0058,0.0028,0.003,0.6119,0.9917,0.8243,0.6299,0.6663,0.4952,0.3451,0.2338,0.1537,0.1372,0.1059,0.0932,0.2432,0.3006,0.2704,0.2103,0.0596,0.0105,0.0067,0.0088,0.0089,0.0108,0.0084,0.0079,0.0057,0.0032,0.0044,0.003,0.0018,0.0035,0.126,0.8047,0.5471,0.1905,0.1781,0.1512,0.1297,0.125,0.0996,0.1018,1,1,1,1,0.8537,0.8106,0.7733,0.714,0.6337,0.5126,0.4252,0.3675,0.5963,0.4882,0.4667,0.0908,0.1107,0.2512,0.3905,0.5846,0.5633,0.4974,0.2823,0.2234,0.3073,0.4833,0.5316,0.3813,0.278,0.2428,0.1915,0.149,0.1716,0.3004,0.3789,0.4765,0.8037,0.7764,0.7569,0.6732,0.7456,0.7082,1,0.9553,1,1,0.5379,0.2345,0.1297,0.0567,0.0064,0.1256,0.1565,0.238,0.3846,0.2614,0.2179,0.1768,0.142,0.2994,0.5863,0.7369,0.6726,0.6013,0.4784,0.4013,0.2237,0.1091,0.0441,0.0552,0.0332,0.0286,0.0324,0.0206,0.013,0.0034,0.0015,0.0014,0.0011,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0146,0.3635,0.1949,0.498,0.8208,1,1,0.8788,0.6189,0.0611,0.0112,0.0069,0.0034,0.1979,0.791,0.6863,0.2378,0.3542,0.5993,0.534,0.488,0.3899,0.3537,0.2956,0.1996,0.1988,0.1962,0.2189,0.5123,1,0.8119,0.5664,0.5503,0.628,0.5862,0.6006,0.4988,0.328,0.2652,0.2112,0.1634,0.1417,0.0856,0.064,0.0496,0.0071,0.0017,0.0032,0.0053,0.0054,0.0061,0.0066,0.0073,0.0088,0.0094,0.0108,0.0079,0.0039,0.002,0.0145,0.7027,1,1,1,1,0.5444,0.2029,0.202,0.177,0.1204,0.089,0.4282,0.5306,0.4205,0.4464,0.2516,0.4812,0.7323,0.5201,0.1112,0.0029,0.001,0.0021,0.0901,0.0718,0.1048,0.7089,0.845,0.7733,0.6227,0.5792,0.1986,0.0125,0.0027,0.0677,0.2826,0.4503,0.15,0.041,0.03,0.1569,0.249,0.5172,0.4127,0.2394,0.1591,0.1434,0.1091,0.2848,0.538,0.5392,0.4876,0.3992,0.4193,0.5255,0.4874,0.4662,0.2934,0.2307,0.144,0.1215,0.1773,0.8407,1,1,0.8165,0.5351,0.4383,0.5576,0.5394,0.3391,0.1548,0.2652,0.25,0.1892,0.1409,0.1207,0.1169,0.0671,0.0292,0.0436,0.0638,0.1015,0.0679,0.3979,0.2957,0.2086,0.1658,0.1924,0.5095,0.6187,0.4547,0.4907,0.4886,0.4568,0.5018,0.4344,0.4923,0.5269,0.611,0.5874,0.6453,0.4909,0.4781,0.3703,0.2635,0.4451,0.4315,0.32,0.3296,0.3613,0.3576,0.3637,0.354,0.3911,0.4264,0.3941,0.1979,0.0273,0.341,0.4423,0.179,0.0827,0.0498,0.1852,0.4017,0.3851,0.4586,0.455,0.455,0.4451,0.3769,0.2996,0.2211,0.1131,0.0838,0.0908,0.0643,0.0478,0.0123,0.0146,0.0112,0.009,0.0483,0.1337,0.2209,0.2495,0.1103,0.0212,0.0024,0.0006,0.0006,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0011,0.0018,0.0285,0.1618,0.2493,0.5878,0.7835,0.7383,0.625,0.5108,0.3396,0.2142,0.0617,0.0348,0.097,0.3064,0.5633,0.5583,0.6678,0.6168,0.5077,0.256,0.0639,0.0132,0.2325,0.1657,0.1664,0.2814,0.4519,0.5975,0.5744,0.5752,0.5124,0.6027,0.5268,0.6062,0.5736,0.5099,0.4171,0.4476,0.5277,0.3497,0.6471,0.5418,0.495,0.4534,0.37,0.3073,0.2545,0.2262,0.2522,0.3312,0.6848,0.831,0.8664,0.9886,1,1,1,1,0.8053,0.2719,0.1949,0.1991,0.2913,0.5387,0.5279,0.4369,0.4943,0.4473,0.4478,0.4493,0.4238,0.3939,0.3951,0.3783,0.993,1,1,1,0.7876,0.1765,0.0812,0.0156,0.0037,0.0016,0.0015,0.0619,0.0584,0.0197,0.0038,0.0022,0.0041,0.0057,0.0066,0.0071,0.0078,0.0091,0.0095,0.0084,0.0081,0.0058,0.0036,0.004,0.0054,0.2041,0.3427,0.5544,0.2462,0.2219,0.6373,0.7568,0.269,0.0713,0.0078,0.2037,0.2972,0.1735,0.4739,0.5672,0.486,0.4498,0.4459,0.3967,0.4686,0.681,0.7377,0.7784,0.8476,0.9518,0.5457,0.3864,0.6961,0.6268,0.5272,0.5707,0.1732,0.3942,0.4646,0.3571,0.3596,0.2378,0.1936,0.1895,0.1797,0.1946,0.1999,0.6102,1,0.767,0.547,0.6811,0.7671,0.6662,0.626,0.7062,0.6675,0.5947,0.5135,0.5267,0.4802,0.396,0.3741,0.2249,0.0466,0.0078,0.0033,0.1926,0.0703,0.4245,0.4691,0.4365,0.2804,0.3954,0.8333,0.6043,0.1644,0.011,0.0555,0.1302,0.7194,0.8248,0.5133,0.363,0.221,0.0587,0.0061,0.0654,0.3635,0.6616,0.5578,0.4599,0.149,0.2016,0.0944,0.0606,0.0246,0.0328,0.0071,0.016,0.0053,0.0027,0.0011,0.0011,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0007,0.0259,0.1969,0.6607,0.6082,0.6101,1,1,0.1408,0.0056,0.2396,0.8504,0.8922,0.4359,0.2495,0.1877,0.1491,0.4862,0.8914,1,1,0.9958,0.9084,0.8392,0.7016,0.4946,0.4991,0.4621,0.3939,0.3956,0.3726,0.309,0.32,0.1547,0.0801,0.0719,0.0332,0.0441,0.0093,0.0042,0.0022,0.0015,0.0017,0.001,0.0012,0.0012,0.0008,0.001,0.0008,0.0018,0.001,0.0035,0.0822,0.2841,0.0857,0.2211,0.8477,0.822,0.805,0.763,0.7168,0.6725,0.6392,0.3728,0.277,0.2009,0.8394,1,1,1,0.9496,0.6685,0.612,0.718,0.4759,0.218,0.3351,0.2447,0.1462,0.084,0.0782,0.0773,0.0189,0.0026,0.0039,0.0049,0.0065,0.0076,0.0081,0.0085,0.0105,0.0132,0.012,0.0166,0.0186,0.0118,0.0685,0.3254,0.677,0.8151,0.9263,0.7397,0.9111,0.6696,0.5808,0.6226,0.5796,0.8206,0.7288,0.8343,0.9161,0.87,1,0.9445,0.7657,0.6179,0.5163,0.2326,0.1398,0.1368,0.1286,0.1118,0.0991,1,0.7676,0.7901,0.6968,0.7064,0.3507,0.0479,0.04,0.2695,0.4935,0.4394,0.3898,0.1677,0.3072,0.3128,0.3401,0.3414,0.3791,0.3749,0.3305,0.2947,0.5008,0.3068,0.0196,0.0044,0.1406,0.1214,0.1345,0.503,0.5365,0.472,0.439,0.4725,0.414,0.3847,0.4213,0.4934,0.5946,0.443,0.2423,0.6681,0.7252,0.4319,0.0164,0.0395,0.0877,0.9913,1,0.8395,0.3687,0.0676,0.056,0.1081,0.4632,0.3998,0.3751,0.2008,0.4852,0.4153,0.3729,0.3721,0.3147,0.2382,0.1791,0.0982,0.1268,0.2313,0.4875,0.5342,0.2236,0.1202,0.1231,0.0951,0.0701,0.4074,0.4046,0.4104,0.3143,0.5498,0.659,0.5943,0.554,0.0938,0.0028,0.1451,0.918,0.7358,0.5321,0.3152,0.4069,0.4416,0.4936,0.2106,0.4072,0.15,0.0982,0.1001,0.058,0.4501,0.7039,0.4128,0.3592,0.334,0.2639,0.1672,0.1398,0.1268,0.0878,0.0891,0.047,0.0359,0.0291,0.0387,0.0092,0.0039,0.0014,0.0012,0.0003,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"peak":0.652305006980896}};window.PRODUCTS=[{"id":"fairborn","title":"Fairborn Type 2 | Phillipe Romano","label":"Fairborn Type 2 · Phillipe Romano","url":"https://www.designerwallcoverings.com/products/prw-920735-rif-003","image_url":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/prw-920735-rif-003.jpg?v=1781716604","file":"assets/products/fairborn.jpg","description":"<p>When light rakes across the embossed surface, it reveals an organic grid-like pattern of broken lines. Although extremely durable — as you’d expect from a Type II vinyl wallcovering named for rock fissures — offering a soft, nature-inspired palette of creams, taupes, blues and browns. A small scale and intricate pattern appear almost seamless, and its 12 two-tone colorways resemble artisanal materials like mosaic</p><br><br>A captivating wallcovering featuring intricate detailing and refined textures, offering a sophisticated backdrop that elevates any interior space. The design evokes timeless elegance with a modern touch.  \n#luxuryinteriors #modernwallcoverings #elegantdesign\n","retrieved_at":"2026-09-05","local":"../assets/products/fairborn.jpg"},{"id":"waterperry","title":"Waterperry Ivory/Stone | Sanderson","label":"Waterperry / Ivory–Stone · Sanderson","url":"https://www.designerwallcoverings.com/products/waterperry-ivory-stone-sanderson","image_url":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/DWAP216280_5340.jpg?v=1788582264","file":"assets/products/waterperry.jpg","description":"<div class=\"sanderson-product\">\n<p>Waterperry in Ivory/Stone is a Sanderson Wallcovering in a botanical/leaves and trees design. Part of the Sanderson heritage collection, this pattern reads in a palette of Alabaster, Linen, Mist. Supplied by the roll; a memo sample is available. An English design-house classic from Sanderson Design Group.</p>\n</div>","retrieved_at":"2026-09-05","local":"../assets/products/waterperry.jpg"},{"id":"wheaton","title":"Wheaton Type 2 | Phillipe Romano","label":"Wheaton Type 2 · Phillipe Romano","url":"https://www.designerwallcoverings.com/products/prw-920166-coa-004-1","image_url":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/d4639abbf9baa5237707.jpg?v=1785438111","file":"assets/products/wheaton.jpg","description":"<p>Weaving shimmering strands and beguiling texture, emulating the intricacy of our favored grasscloth Tide in the form of a durable Type II vinyl</p>\n","retrieved_at":"2026-09-05","local":"../assets/products/wheaton.jpg"},{"id":"topiary","title":"Topiary View Thyme | Sanderson","label":"Topiary View / Thyme · Sanderson","url":"https://www.designerwallcoverings.com/products/topiary-view-thyme-sanderson","image_url":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/DHIP217511_f500.jpg?v=1788582179","file":"assets/products/topiary.jpg","description":"<div class=\"sanderson-product\">\n<p>Topiary View in Thyme is a Sanderson Wallcovering in a landscapes design. Part of the Sanderson heritage collection, this pattern reads in a palette of Alabaster, Stone, Oatmeal. Supplied by the roll; a memo sample is available. An English design-house classic from Sanderson Design Group.</p>\n</div>","retrieved_at":"2026-09-05","local":"../assets/products/topiary.jpg"},{"id":"kaya","title":"Kaya - Cream Weave Wallcovering | Malibu Wallcovering","label":"Kaya / Cream Weave · Malibu","url":"https://www.designerwallcoverings.com/products/kaya-cream-weave-wallcovering-malibu-wallpaper","image_url":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/0086435_kaya-cream-weave_650.jpg?v=1776906632","file":"assets/products/kaya.jpg","description":"<p>Elevate your space with the luxurious Kaya Cream Weave wallcovering. This exquisite nonwoven vinyl design features a warm neutral color palette and stunning texture, perfect for creating an elegant ambiance.</p>","retrieved_at":"2026-09-05","local":"../assets/products/kaya.jpg"}];/* Wallo Material Notes: deterministic articulated character acting and real DW samples. */
(() => {
 'use strict';
 const canvas=document.getElementById('stage'), c=canvas.getContext('2d');
 const E=window.EPISODE, P=window.PRODUCTS, ep=E.index;
 const ink='#243a3b', chalk='#eeeae2', teal='#284f4b', clay='#a9634d', brass='#b7924f';
 const images={}; let telemetry=[];
 const clamp=(x,a=0,b=1)=>Math.max(a,Math.min(b,x));
 const mix=(a,b,p)=>a+(b-a)*p;
 const smooth=p=>{p=clamp(p);return p*p*(3-2*p);};
 const ease=(t,a,b)=>smooth((t-a)/(b-a));
 const pulse=(t,a,b,c,d)=>ease(t,a,b)*(1-ease(t,c,d));
 const point=(x,y)=>({x,y});
 const dist=(a,b)=>Math.hypot(a.x-b.x,a.y-b.y);
 function group(x,y,r,s,fn){c.save();c.translate(x,y);c.rotate(r);c.scale(s,s);fn();c.restore();}
 function path(fn,fill,stroke=null,w=2){c.beginPath();fn(c);if(fill){c.fillStyle=fill;c.fill();}if(stroke){c.lineWidth=w;c.strokeStyle=stroke;c.lineJoin='round';c.lineCap='round';c.stroke();}}
 function line(x,y,u,v,col,w=2){path(q=>{q.moveTo(x,y);q.lineTo(u,v);},null,col,w);}
 function rect(x,y,w,h,col){c.fillStyle=col;c.fillRect(x,y,w,h);}
 function oval(x,y,rx,ry,col,stroke=null,w=2){path(q=>q.ellipse(x,y,rx,ry,0,0,Math.PI*2),col,stroke,w);}
 function rr(x,y,w,h,r,col,stroke=null,lw=2){path(q=>q.roundRect(x,y,w,h,r),col,stroke,lw);}
 function text(s,x,y,size=28,color=ink,font='Verdana',align='left'){c.font=`${size}px '${font}'`;c.textAlign=align;c.textBaseline='alphabetic';c.fillStyle=color;c.fillText(s,x,y);}
 function wrap(s,x,y,max,size=36,color=ink,font='Verdana',lh=size*1.4){
   c.font=`${size}px '${font}'`;let row='',lines=[];
   for(const word of s.split(' ')){const test=row?row+' '+word:word;if(c.measureText(test).width>max&&row){lines.push(row);row=word;}else row=test;}
   if(row)lines.push(row);lines.forEach((v,i)=>text(v,x,y+i*lh,size,color,font));return lines.length;
 }
 function imageCover(id,x,y,w,h){const im=images[id];if(!im||!im.complete)return;const k=Math.max(w/im.width,h/im.height);c.save();c.beginPath();c.rect(x,y,w,h);c.clip();c.drawImage(im,x+(w-im.width*k)/2,y+(h-im.height*k)/2,im.width*k,im.height*k);c.restore();}
 function sample(id,x,y,w=126,h=164,r=0){group(x,y,r,1,()=>{rr(-w/2+7,-h/2+9,w,h,2,'#203a3a20');rect(-w/2-5,-h/2-5,w+10,h+10,'#f9f7f0');imageCover(id,-w/2,-h/2,w,h);line(-w/2,-h/2,w/2,-h/2,'#ffffff',2);});}
 function bone(a,b,width,col,shade){
   const angle=Math.atan2(b.y-a.y,b.x-a.x),d=dist(a,b);
   group(a.x,a.y,angle,1,()=>{rr(-width*.18,-width/2,d+width*.36,width,width*.46,col);path(q=>{q.moveTo(5,width*.26);q.quadraticCurveTo(d*.5,width*.34,d-3,width*.22);},null,shade||'#00000018',2.3);});
 }
 function solveIK(root,target,l1,l2,pole){
   const dx=target.x-root.x,dy=target.y-root.y,raw=Math.hypot(dx,dy),d=clamp(raw,Math.abs(l1-l2)+.01,l1+l2-.01);
   const nx=raw?dx/raw:0,ny=raw?dy/raw:1,a=(l1*l1-l2*l2+d*d)/(2*d),h=Math.sqrt(Math.max(0,l1*l1-a*a));
   const end=point(root.x+nx*d,root.y+ny*d),joint=point(root.x+nx*a-ny*h*pole,root.y+ny*a+nx*h*pole);
   return {root,joint,end,l1,l2};
 }
 function rotatePoint(p,angle,origin=point(0,-258)){const x=p.x-origin.x,y=p.y-origin.y;return point(origin.x+x*Math.cos(angle)-y*Math.sin(angle),origin.y+x*Math.sin(angle)+y*Math.cos(angle));}
 function key(t,frames){if(t<=frames[0][0])return frames[0].slice(1);for(let i=1;i<frames.length;i++)if(t<=frames[i][0]){const p=ease(t,frames[i-1][0],frames[i][0]);return frames[i].slice(1).map((v,j)=>mix(frames[i-1][j+1],v,p));}return frames.at(-1).slice(1);}
 function gait(t,start,end,x0,x1){
   const p=clamp((t-start)/(end-start));
   const lift=(a,b)=>p>a&&p<b?Math.sin(Math.PI*(p-a)/(b-a))*44:0;
   const left=mix(x0-30,(x0+x1)/2-30,smooth((p-.08)/.28))+mix(0,(x1-x0)/2,smooth((p-.65)/.3));
   const right=mix(x0+30,x1+30,smooth((p-.35)/.3));
   return {x:(left+right)/2,feet:[point(left,-lift(.08,.36)-lift(.65,.95)),point(right,-lift(.35,.65))],walking:p>0&&p<1,phase:p};
 }
 function voiceEnergy(t){const env=E.audio?.energy||[];return env[Math.min(env.length-1,Math.max(0,Math.floor(t*50)))]||0;}
 function hand(x,y,r,pose='open',skin='#c89472',prop=null){
   group(x,y,r,1,()=>{
    if(prop==='sample')sample(E.product,8,-18,115,148,-.04);
    if(prop==='fan')for(let k=0;k<3;k++)sample(['waterperry','topiary','kaya'][k],k*17-15,-43,91,130,(k-1)*.18);
    if(prop==='report'){rr(-12,-134,135,176,4,'#f8f5e9',ink,2);rect(1,-117,108,5,teal);text('TEST',4,-87,22,ink);text('REPORT',4,-58,22,ink);for(let k=0;k<3;k++)line(4,-35+k*18,97,-35+k*18,'#a4aaa0',2);}
    if(prop==='cloth'){path(q=>{q.moveTo(-28,-15);q.lineTo(30,-26);q.lineTo(43,35);q.lineTo(-21,44);q.closePath();},'#c8c5b5',ink,1);line(-17,-4,28,25,'#efeadb',3);}
    if(prop==='roller'){line(0,-8,0,-72,ink,7);line(0,-72,37,-72,ink,7);line(37,-72,37,-108,ink,5);rr(-27,-132,127,38,11,'#8b9c8d',ink,2);rr(-9,-9,18,54,7,brass);}
    if(prop==='tablet'){rr(-73,-76,145,170,8,ink);rr(-65,-67,129,151,4,'#f4f0e5');text('DW',-45,-33,27,teal,'Georgia');for(let i=0;i<3;i++)imageCover(['waterperry','fairborn','kaya'][i],-47+i*32,-6,27,60);}
    if(pose==='point'){bone(point(-4,3),point(34,-18),13,skin);bone(point(0,9),point(16,15),21,skin);oval(-6,9,13,18,skin);}
    else{rr(-14,-9,29,42,12,skin);oval(-15,5,7,13,skin);for(let i=0;i<3;i++)line(-7+i*6,14,-7+i*6,28,'#9e685446',1);}
   });
 }
 function head(kind,x,y,t,look,mouth,tilt){
   const male=kind==='wallo',skin=male?'#c89472':'#d5a185',dark=male?'#714f3b':'#834e39';
   group(x,y,tilt,.82,()=>{
    if(!male){oval(-4,-10,51,70,dark);oval(-39,-50,28,28,dark);path(q=>{q.moveTo(-57,-55);q.quadraticCurveTo(-81,-37,-59,-16);},null,'#a86b4b',7);}
    // Tapered jaw, defined brow and modest nose maintain adult proportions.
    path(q=>{q.moveTo(-41,-40);q.bezierCurveTo(-43,-77,43,-80,44,-34);q.lineTo(40,31);q.quadraticCurveTo(31,66,5,72);q.quadraticCurveTo(-26,65,-36,34);q.closePath();},skin);
    path(q=>{q.moveTo(-38,-31);q.lineTo(-32,34);q.quadraticCurveTo(-24,52,-8,57);q.lineTo(-20,21);q.lineTo(-18,-46);q.closePath();},male?'#b58162':'#c39076');
    oval(-40,3,9,16,skin);oval(42,2,8,15,skin);
    if(male){path(q=>{q.moveTo(-39,-39);q.lineTo(-33,-18);q.lineTo(-30,-54);q.lineTo(31,-54);q.lineTo(43,-25);q.lineTo(42,-55);q.closePath();},dark);}
    else{path(q=>{q.moveTo(-40,-27);q.quadraticCurveTo(-24,-63,2,-56);q.quadraticCurveTo(38,-37,43,-19);q.lineTo(38,-58);q.quadraticCurveTo(-4,-89,-37,-55);q.closePath();},dark);for(let i=0;i<4;i++)path(q=>{q.moveTo(-29+i*17,-56);q.quadraticCurveTo(-26+i*17,-43,-13+i*17,-39);},null,'#ae7150',2);}
    const blink=(t+(male?.31:1.93))%4.4<.14;
    for(const side of [-1,1]){const ex=side*18+look*3,ey=-8;path(q=>{q.moveTo(ex-10,ey);q.quadraticCurveTo(ex,ey-5,ex+10,ey);},null,ink,2.2);if(!blink){oval(ex+look*3,ey+1,3.5,4.3,ink);oval(ex+look*3+1,ey-.3,1.1,1.2,'#eee8dc');}line(ex-10,ey-15,ex+8,ey-17+(side===1?look*2:0),dark,2.5);}
    path(q=>{q.moveTo(3+look*5,-5);q.lineTo(8+look*8,15);q.quadraticCurveTo(4+look*5,20,-1+look*5,17);},null,'#98644f',1.8);
    if(male){path(q=>{q.moveTo(-20,29);q.quadraticCurveTo(-6,20,4,27);q.quadraticCurveTo(15,23,26,30);q.quadraticCurveTo(17,39,3,34);q.quadraticCurveTo(-9,37,-20,29);},dark);}
    if(mouth>.12){oval(4,44,10+mouth*3,2+mouth*6,'#714735');if(mouth>.4)line(-2,41,11,41,'#efe6d7',2);}
    else path(q=>{q.moveTo(-7,43);q.quadraticCurveTo(5,48,17,42);},null,'#714735',1.8);
    if(male){path(q=>{q.moveTo(-49,-53);q.quadraticCurveTo(-48,-98,10,-95);q.quadraticCurveTo(47,-89,48,-53);q.closePath();},brass);path(q=>{q.moveTo(-49,-53);q.quadraticCurveTo(12,-64,72,-48);q.quadraticCurveTo(77,-35,39,-41);q.lineTo(-49,-43);q.closePath();},'#97773f');line(-27,-73,12,-84,'#d2b270',3);}
    else oval(-39,14,3.5,5.5,brass);
   });
 }
 function character(kind,t,settings={}){
   const male=kind==='wallo',skin=male?'#c89472':'#d5a185',shirt=male?'#d6d0bc':clay,cloth=male?teal:'#c8c1af';
   const walk=settings.walk||gait(t,0,1,settings.x,settings.x);let x=walk.x,y=settings.y||882;
   const crouch=settings.crouch||0,lean=settings.lean||0,breath=Math.sin(t*2.1+(male?0:1.2))*1.7;
   const hipY=-275+crouch,origin=point(0,hipY);
   const bodyPoint=(u,v)=>rotatePoint(point(u,v+crouch+breath),lean,origin);
   const leftShoulder=bodyPoint(-62,-469),rightShoulder=bodyPoint(62,-469);
   const arms=[solveIK(leftShoulder,point(...settings.left),118,111,-1),solveIK(rightShoulder,point(...settings.right),118,111,1)];
   const legs=walk.feet.map((foot,i)=>solveIK(point(i===0?-32:32,hipY),point(foot.x-x,foot.y-13),134,136,i===0?1:-1));
   const mouth=male?voiceEnergy(t)*.88:0;
   const joints={kind,x,y,arms,legs,lean,crouch,feet:walk.feet,walking:walk.walking,props:settings.props||[]};
   telemetry.push(joints);
   oval(x,y+6,94-crouch*.1,15,'#233a3b15');
   group(x,y,0,settings.scale||1,()=>{
    // Back arm and far leg precede torso so elbow overlaps read spatially.
    const armDraw=(i)=>{const b=arms[i];bone(b.root,b.joint,39,shirt);bone(b.joint,b.end,27,skin);oval(b.joint.x,b.joint.y,16,16,skin);const sleeve=point(mix(b.root.x,b.joint.x,.77),mix(b.root.y,b.joint.y,.77));bone(b.root,sleeve,43,shirt);line(sleeve.x-8,sleeve.y,sleeve.x+9,sleeve.y,'#ffffff30',2);};
    armDraw(0);
    legs.forEach((b,i)=>{bone(b.root,b.joint,58,cloth);bone(b.joint,b.end,43,cloth);oval(b.joint.x,b.joint.y,23,23,cloth);line(b.root.x-8,b.root.y+30,b.joint.x-6,b.joint.y-16,male?'#41635b':'#dfd8c8',2);group(b.end.x,b.end.y+10,walk.walking&&walk.feet[i].y<0?-.12:0,1,()=>{path(q=>{q.moveTo(-24,-20);q.lineTo(20,-20);q.lineTo(27,-9);q.quadraticCurveTo(60,-5,56,14);q.lineTo(-24,14);q.closePath();},male?'#655545':'#626b62');line(-24,14,55,14,'#34413d',5);line(4,-8,25,-7,'#c6b69b',2);});});
    group(0,hipY,lean,1,()=>{
     const dy=crouch+breath-hipY;
     path(q=>{q.moveTo(-63,-478+dy);q.quadraticCurveTo(-82,-460+dy,-71,-417+dy);q.lineTo(-60,-266+dy);q.quadraticCurveTo(0,-235+dy,60,-266+dy);q.lineTo(71,-417+dy);q.quadraticCurveTo(79,-460+dy,58,-479+dy);q.closePath();},shirt);
     // Structured overall/apron silhouette, stitched seams, pockets and cloth folds.
     path(q=>{q.moveTo(-50,-425+dy);q.lineTo(-46,-350+dy);q.lineTo(-61,-290+dy);q.lineTo(-60,-253+dy);q.lineTo(61,-253+dy);q.lineTo(61,-290+dy);q.lineTo(46,-350+dy);q.lineTo(48,-425+dy);q.closePath();},cloth);
     for(const s of [-1,1]){line(s*44,-472+dy,s*38,-405+dy,cloth,17);oval(s*39,-414+dy,4.8,4.8,brass);line(s*49,-328+dy,s*41,-312+dy,male?'#59756b':'#a9a18f',2);}
     rr(-27,-401+dy,54,56,4,male?'#254540':'#bab39f');path(q=>{q.moveTo(-23,-388+dy);q.quadraticCurveTo(0,-376+dy,24,-388+dy);},null,male?'#6a8778':'#e5dfd0',1.5);
     line(-50,-266+dy,52,-266+dy,male?'#1e403b':'#aca590',3);
     if(male){rr(-66,-321+dy,20,67,3,'#8d7452');line(-56,-342+dy,-56,-282+dy,brass,6);}
     path(q=>{q.moveTo(-17,-509+dy);q.lineTo(-18,-474+dy);q.quadraticCurveTo(0,-454+dy,21,-474+dy);q.lineTo(18,-509+dy);q.closePath();},skin);
    });
    const hp=bodyPoint(0,-559);head(kind,hp.x,hp.y,t,settings.look||0,mouth,(settings.headTilt||0)+lean*.28);
    armDraw(1);
    arms.forEach((b,i)=>hand(b.end.x,b.end.y,(settings.wrists||[0,0])[i],(settings.gestures||['open','open'])[i],skin,(settings.props||[])[i]));
   });
   return joints;
 }
 function room(t){
   rect(-350,-300,1800,1200,'#dfdbd1');rect(-350,765,1800,450,'#c9c2b1');
   rect(20,0,240,706,'#e9e7dc');for(let i=0;i<4;i++)line(22+i*59,0,22+i*59,704,'#f7f4e9',7);
   rect(318,26,647,727,'#c7bfae');rect(329,37,624,704,'#efede3');
   if(ep===1){const a=ease(t,5.1,8);rect(343,49,596,678,'#aab7a4');c.save();c.beginPath();c.rect(343,49,596*a,678);c.clip();imageCover(E.product,343,49,596,678);c.restore();}
   else imageCover(E.product,343,49,596,678);
   line(340,743,943,743,'#faf7eb',4);line(-350,765,1450,765,'#a79f8d',2);
   for(let i=0;i<6;i++)line(-200,814+i*54,1400,814+i*54,'#b6ad9a',1.4);
   line(91,765,-170,1220,'#b6ad9a',1.4);line(880,765,1130,1220,'#b6ad9a',1.4);
   // Light and shadow move gently with the camera, not with an arbitrary pulse.
   path(q=>{q.moveTo(40,706);q.lineTo(263,706);q.lineTo(672,1120);q.lineTo(412,1120);q.closePath();},'#fcf7df28');
   // A sample ledge ties the real products to the working material library.
   rect(404,685,479,12,'#b29d7a');for(let i=0;i<3;i++)sample(P[(i+ep)%P.length].id,468+i*115,622,88,116,(i-1)*.045);
 }
 function documentPanel(t){
   if(ep!==0)return;
   const a=pulse(t,4.2,4.9,14.3,15.0);if(!a)return;
   group(640,177+(1-a)*28,0,.88,()=>{
    rr(-239,-168,478,336,2,'#f7f4e9',ink,1.5);text('BEFORE YOU SPECIFY',-210,-126,24,teal);
    const labels=['PRODUCT / TEST REPORT','CLASSIFICATION','SUBSTRATE + ADHESIVE'];
    labels.forEach((label,i)=>{const done=t>5.0+i*2.8;line(-209,-61+i*77,-182,-61+i*77,done?brass:'#bac0b5',3);text(label,-163,-52+i*77,21,ink);});
   });
 }
 function swatchBoard(t){
   if(ep!==3&&ep!==4)return;
   const a=pulse(t,6.4,7.1,13.4,14);if(!a)return;
   group(560,275+(1-a)*25,0,.95,()=>{
    rr(-199,-139,398,278,2,'#f5f0e4',ink,1);sample(E.product,-83,-1,155,220,0);
    rect(28,-111,137,83,'#c6bea9');rect(28,-16,137,69,'#8c9985');rect(28,64,137,43,'#b89672');
    text(ep===3?'COMPARE YOUR FINISHES':'YOUR PROJECT SHORTLIST',-192,-162,23,ink);
   });
 }
 function actionNotes(t){
   if(ep!==2)return;const a=pulse(t,8,8.5,14.2,14.8);if(!a)return;
   ['ABRASION','CLEANABILITY','CARE'].forEach((s,i)=>{const show=ease(t,8+i*.7,8.4+i*.7);if(show){rect(440,120+i*77,440,58,'#f4f0e6');text(s,468,159+i*77,27,ink);}});
 }
 function performances(t){
   let wwalk=gait(t,.2,2.9,126,253),pwalk=gait(t,.5,3.1,949,801);
   const w={walk:wwalk,left:[-85,-279],right:[149,-396],wrists:[-.1,-.36],props:[],gestures:['open','open'],look:.6,lean:Math.sin(t*.8)*.012};
   const p={walk:pwalk,left:[-130,-402],right:[86,-275],wrists:[.14,.08],props:['sample'],look:-.6,lean:Math.sin(t*.73+1)*.013};
   // Presenter gestures are deliberately different for each spoken clause.
   const gesture=key(t,[[0,100,-285],[2.8,145,-411],[4.4,165,-440],[5.2,120,-367],[8.7,161,-441],[9.6,149,-378],[12.8,180,-432],[14,125,-360],[17,152,-419],[19.8,126,-383]]);
   w.right=gesture;w.wrists[1]=key(t,[[0,0],[3,-.5],[5.2,-.15],[8,-.42],[11,.1],[14,-.4],[18,-.2]])[0];
   w.gestures[1]=t>4.5&&t<8.8?'point':'open';w.headTilt=Math.sin(t*.65)*.022;w.look=t>4&&t<9?1:.1;
   if(t<3){w.right=[90-Math.sin(t*4)*40,-277];w.left=[-90+Math.sin(t*4)*40,-277];p.left=[-90+Math.sin(t*4)*24,-280];p.props=[];}
   if(ep===0){p.props=['report'];p.left=key(t,[[0,-85,-280],[3,-130,-365],[6,-131,-420],[10,-153,-445],[14,-125,-384],[19,-108,-376]]);p.wrists[0]=-.14;p.right=key(t,[[0,90,-277],[8,98,-320],[11,-10,-377],[14,93,-278]]);}
   if(ep===1){
    p.props=t<8?['roller']:['sample'];p.left=key(t,[[0,-88,-283],[3,-129,-447],[4.5,-142,-495],[5.2,-143,-411],[6,-146,-495],[7,-145,-397],[8,-100,-367],[11,-139,-436],[15,-103,-361]]);p.wrists[0]=.07;
    if(t>9.3){p.walk=gait(t,9.3,11.4,801,870);p.look=-1;p.lean=-.06*ease(t,10,11);}
    w.props=['sample'];w.left=key(t,[[0,-87,-282],[4,-98,-291],[6,-39,-389],[8,-30,-433],[12,-59,-386],[17,-78,-320]]);w.wrists[0]=-.08;
   }
   if(ep===2){
    p.crouch=pulse(t,4,5.6,8.8,10.2)*113;p.lean=-pulse(t,4,5.4,9,10.3)*.10;
    const wipe=pulse(t,5,5.8,8.2,9);p.props=['cloth'];p.left=[-147-wipe*17,-389+p.crouch+wipe*Math.sin(t*6)*42];p.wrists[0]=-.5;
    p.right=[85,-284+p.crouch];p.look=-1;
    w.props=['sample'];w.left=[-45,-371];w.wrists[0]=-.18;
   }
   if(ep===3){
    p.props=['sample'];p.left=key(t,[[0,-92,-280],[3,-100,-366],[6,-138,-440],[10,-144,-478],[14,-126,-418],[18,-93,-340]]);p.wrists[0]=key(t,[[0,0],[3,-.08],[7,.22],[10,-.18],[13,.16],[17,0]])[0];
    w.props=['fan'];w.left=key(t,[[0,-90,-280],[4,-33,-365],[7,-44,-400],[12,-24,-389],[16,-44,-346]]);w.wrists[0]=-.13;
   }
   if(ep===4){
    w.props=['tablet'];w.left=[-43,-341];w.right=key(t,[[0,101,-280],[3,143,-413],[6,158,-444],[8.5,65,-361],[10,34,-340],[11.6,20,-356],[13,144,-411],[17,160,-444],[19,112,-374]]);w.gestures[1]=t>8&&t<12?'point':'open';
    p.props=['fan'];p.left=[-129,-400];p.right=key(t,[[0,89,-280],[6,86,-280],[9,-18,-384],[12,76,-285],[17,100,-382],[19,86,-297]]);
   }
   // Camera-right actor is drawn first so the presenter remains the visual lead.
   character('pippa',t,p);character('wallo',t,w);
 }
 function overlay(t){
   rect(0,0,1080,437,chalk);rect(0,1437,1080,483,chalk);
   text('DESIGNER WALLCOVERINGS',72,96,25,ink);text('WALLO PRESENTS',1008,96,21,teal,'Verdana','right');
   line(72,124,1008,124,'#b9b6aa',1);text(E.kicker,72,178,24,teal);
   E.title.split('\n').forEach((s,i)=>text(s,69,270+i*87,73,ink,'Georgia'));
   text('MATERIAL NOTES',73,411,22,teal);text('WITH WALLO & PIPPA',1006,411,20,teal,'Verdana','right');
   const product=P.find(p=>p.id===E.product);text(product.label,72,1481,25,ink);
   line(72,1505,1008,1505,'#b9b6aa',1);
   const segment=(E.audio?.segments||[]).filter(s=>t>=s.start).at(-1);
   const caption=segment?E.captions[segment.index]:'A wallcovering presentation with Wallo.';
   wrap(caption,72,1570,930,38,ink,'Verdana',55);
   if(t>=16.8)text(E.takeaway,72,1738,37,teal,'Georgia');
   wrap(E.note,72,1794,926,24,'#58635b','Verdana',34);
   text('designerwallcoverings.com',72,1870,25,teal);
   text(String(ep+1).padStart(2,'0')+' / 05',1008,1870,22,teal,'Verdana','right');
   rect(72,1894,936,3,'#c9c7bb');rect(72,1894,936*clamp(t/20),3,teal);
 }
 function draw(t){
   telemetry=[];rect(0,0,1080,1920,chalk);
   c.save();c.beginPath();c.rect(0,437,1080,1000);c.clip();
   const zoom=1+ease(t,3.3,5)*.08-ease(t,14.2,16)*.06;
   c.translate(540,950);c.scale(zoom,zoom);c.translate(-540,-487);
   room(t);documentPanel(t);swatchBoard(t);actionNotes(t);performances(t);
   c.restore();overlay(t);window.__rigState=telemetry;
 }
 const driver={t:0};window.__timelines=window.__timelines||{};
 window.__timelines.main=gsap.timeline({paused:true}).to(driver,{t:20,duration:20,ease:'none',onUpdate:()=>draw(driver.t)},0);
 window.__draw=draw;
 window.__ready=Promise.all(P.map(p=>new Promise((resolve,reject)=>{const im=new Image();images[p.id]=im;im.onload=resolve;im.onerror=()=>reject(new Error('Missing product '+p.id));im.src=p.local;}))).then(()=>document.fonts.ready).then(()=>{draw(driver.t);return true;});
 draw(0);
})();
</script></body></html>