← back to Wallpaper Paint Antics
series2/episodes/05-wallpaper-or-paint.html
215 lines
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=1080,height=1920"><title>Wallo presents — Why specify wallpaper over paint?</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 Why specify wallpaper over paint?"></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-1.wav" data-start="0" data-duration="20" data-track-index="1"></audio></div><script>window.EPISODE={"slug":"05-wallpaper-or-paint","title":"Color is only\nthe beginning.","shortTitle":"Why specify wallpaper over paint?","kicker":"02 / DESIGN BENEFITS","product":"waterperry","note":"Waterperry / Ivory–Stone · Sanderson. Illustration not to scale.","takeaway":"Give the room a point of view.","lines":["Paint gives you color. Wallcovering can give you another dimension.","Pattern creates rhythm. Texture changes how a surface catches light.","This Sanderson design makes one wall the room's focal point.","Choose wallpaper for character, and paint where you want simplicity."],"captions":["Color is the starting point.","Pattern. Texture. Depth.","One wall can define the room.","Wallpaper for character. Paint for simplicity."],"index":1,"audio":{"slug":"05-wallpaper-or-paint","provider":"Kokoro local","voice":"am_michael","duration":20,"segments":[{"index":0,"start":0.6,"end":4.483,"text":"Paint gives you color. Wallcovering can give you another dimension.","caption":"Color is the starting point.","duration":3.883},{"index":1,"start":5.1,"end":9.324,"text":"Pattern creates rhythm. Texture changes how a surface catches light.","caption":"Pattern. Texture. Depth.","duration":4.224},{"index":2,"start":9.6,"end":13.696,"text":"This Sanderson design makes one wall the room's focal point.","caption":"One wall can define the room.","duration":4.096},{"index":3,"start":14.1,"end":18.025,"text":"Choose wallpaper for character, and paint where you want simplicity.","caption":"Wallpaper for character. Paint for simplicity.","duration":3.925}],"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.0009,0.1389,0.1517,0.2161,0.4266,0.6739,0.6462,0.5771,0.6139,0.5614,0.433,0.2164,0.1501,0.0539,0.0411,0.0399,0.0431,0.0354,0.1053,0.2243,0.6331,0.7637,0.763,0.7776,0.2153,0.151,0.2944,0.4863,0.4505,0.3431,0.185,0.21,0.3098,0.3164,0.2116,0.0714,0.0078,0.0056,0.1376,0.2764,0.1865,0.2572,0.2807,0.2333,0.2763,0.6548,1,1,1,0.7987,0.5067,0.2944,0.2674,0.2038,0.1339,0.11,0.0764,0.0372,0.0147,0.0258,0.0142,0.0091,0.0021,0.0011,0.0013,0.0016,0.002,0.0026,0.0029,0.0033,0.0038,0.0049,0.0061,0.0069,0.0093,0.0082,0.0103,0.0119,0.0063,0.002,0.0032,0.0023,0.1299,0.2004,0.2621,0.4307,0.8747,1,1,1,1,1,1,1,1,0.2146,0.0166,0.0034,0.0024,0.3216,0.2786,0.7684,0.2106,0.1688,0.1775,0.1863,0.2622,0.4009,0.428,0.4323,0.4266,0.4068,0.4082,0.4208,0.375,0.2756,0.1075,0.0099,0.1278,0.112,0.292,0.4602,0.3299,0.3536,0.3732,0.3996,0.2945,0.192,0.1429,0.2159,0.4804,0.6552,0.741,0.6986,0.1646,0.1349,0.1213,0.1332,0.152,0.2048,0.2626,0.329,0.3624,0.3629,0.3881,0.3287,0.3673,0.3726,0.3997,0.4195,0.3788,0.4222,0.4985,0.4664,0.5747,0.6037,0.1863,0.1492,0.4204,0.4614,0.4105,0.3201,0.2066,0.1544,0.1624,0.4284,0.4281,0.3571,0.4048,0.4244,0.4703,0.4054,0.4486,0.462,0.4331,0.3091,0.2819,0.0874,0.1378,0.4752,0.6097,0.6132,0.3525,0.0734,0.1871,0.0938,0.0559,0.0252,0.0338,0.0075,0.0166,0.0055,0.0029,0.0011,0.0004,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.0007,0.0124,0.1816,0.3305,0.4079,0.57,0.9222,0.8808,0.8585,0.7219,0.5228,0.2082,0.6079,0.6087,0.6309,0.5995,0.5675,0.5909,0.5489,0.3951,0.1521,0.0837,0.0489,0.0958,0.0936,0.3407,0.3996,0.392,0.3049,0.3854,0.5193,0.6533,0.5779,0.5296,0.5214,0.4893,0.3329,0.1213,0.0465,0.3398,0.2913,0.0841,0.0814,0.1494,0.1888,0.2429,0.3568,0.6675,1,1,1,0.4725,0.2437,0.1453,0.3793,0.2467,0.1728,0.0516,0.0263,0.0491,0.0408,0.0286,0.0309,0.0054,0.001,0.0013,0.0014,0.0016,0.0019,0.0021,0.0027,0.0034,0.0047,0.0053,0.0062,0.0078,0.0081,0.0067,0.0044,0.0086,0.0041,0.0018,0.066,0.2806,0.61,1,1,1,0.4813,0.0257,0.0772,0.1589,0.6571,0.4209,0.0663,0.0291,0.0822,0.275,0.4429,0.5916,0.5074,0.2999,0.1229,0.0153,0.0873,0.4598,0.5817,0.4037,0.3386,0.4669,0.6258,0.4973,0.4756,0.4904,0.5048,0.4834,0.5658,0.5097,0.5034,0.4594,0.2793,0.2598,0.2339,0.3842,0.4001,0.3637,0.3051,0.241,0.1865,0.2266,0.1016,0.1232,0.1919,0.2406,0.3852,0.4074,0.3841,0.4645,0.4875,0.4188,0.3531,0.3852,0.4183,0.3006,0.3745,0.4269,0.3591,0.3435,0.2982,0.272,0.2313,0.5985,0.9521,0.8054,0.8367,0.6945,0.6582,0.134,0.1047,0.0954,0.0726,0.2529,0.3523,0.3661,0.2502,0.2854,0.2723,0.1879,0.0208,0.006,0.0825,0.1042,0.0867,0.3597,0.415,0.3577,0.3488,0.4695,0.2983,0.03,0.0482,0.305,0.1933,0.2001,0.284,0.2558,0.243,0.2125,0.1811,0.1873,0.3172,0.3736,0.4281,0.5689,0.6375,0.8002,0.8189,0.772,0.3907,0.2836,0.1096,0.0718,0.0455,0.0166,0.0111,0.0128,0.0041,0.0013,0.0022,0.0008,0.0011,0.0005,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0011,0.0029,0.0557,0.2103,0.8746,1,0.8395,0.5963,0.8625,0.788,0.5799,0.4302,0.3683,0.3325,0.3073,0.6046,1,0.6848,0.7188,0.8196,0.7963,0.7829,0.7007,0.6394,0.3486,0.2692,0.6223,0.6302,0.4384,0.3552,0.6189,0.5471,0.5152,0.3375,0.2218,0.4245,0.4069,0.4702,0.4526,0.4177,0.3072,0.2324,0.2125,0.3909,0.5216,0.4742,0.368,0.3188,0.29,0.3059,0.3688,0.8644,0.7728,0.7514,0.7627,0.6532,0.7537,0.7418,0.7562,0.5359,0.3775,0.2997,0.248,0.2106,0.1804,0.1687,0.1277,0.0287,0.0103,0.0081,0.0116,0.0106,0.0093,0.0084,0.006,0.0085,0.0063,0.0055,0.2178,0.5037,0.5147,0.6706,0.4113,0.1395,0.1048,0.4407,0.6923,0.4845,0.092,0.0594,0.1906,0.2805,0.3104,0.5378,0.6701,0.9309,0.7825,0.7659,0.7138,0.7314,0.6515,0.5834,0.5421,0.4886,0.4144,0.2993,0.3044,0.5034,0.8413,0.9742,1,1,1,1,1,0.8349,0.6758,0.3405,0.2915,0.2431,0.1153,0.0893,0.0156,0.0032,0.0056,0.0073,0.0078,0.0074,0.0072,0.0041,0.0029,0.0032,0.0026,0.0018,0.0025,0.1047,0.4938,0.5581,0.3837,0.303,0.2594,0.2486,0.2992,0.4674,0.5778,0.7377,0.7841,0.7696,0.7744,0.6577,0.6135,0.5415,0.4837,0.514,0.4367,0.3495,0.2887,0.5585,0.5119,0.1609,0.1262,0.0949,0.0679,0.0701,0.8694,0.7886,0.7509,0.5974,0.2547,0.0749,0.0071,0.162,0.1265,0.3658,0.4008,0.3396,0.1303,0.0315,0.0043,0.0056,0.1676,0.0884,0.1233,0.0659,0.5457,0.8468,0.833,0.7201,0.4761,0.2168,0.1211,0.0677,0.0429,0.0178,0.0236,0.0123,0.0113,0.0025,0.0017,0.0249,0.017,0.0043,0.0015,0.0011,0.0008,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0009,0.0386,0.1824,0.6817,0.7273,0.5769,0.3643,0.562,0.5896,0.5214,0.4846,0.535,0.4699,0.3802,0.5335,0.2933,0.2917,0.282,0.3172,0.3253,0.6864,1,1,1,1,1,1,1,1,1,0.6132,0.1338,0.0275,0.0049,0.2359,0.192,0.4487,0.5891,0.5862,0.6167,0.5753,0.4624,0.1265,0.019,0.0066,0.1695,0.4457,0.5648,0.5228,0.2751,0.1806,0.1816,0.189,0.1104,0.3541,0.33,0.1795,0.0691,0.011,0.1234,0.132,0.1714,0.5335,0.7031,0.7372,0.6638,0.6932,0.8441,0.8036,0.8259,0.7696,0.5486,0.3022,0.0628,0.0136,0.007,0.0017,0.0015,0.1605,0.3318,0.3296,0.2849,0.2103,0.0983,0.0847,0.0586,0.0626,0.0163,0.0024,0.0029,0.0039,0.0046,0.0055,0.0052,0.0053,0.0058,0.0059,0.0076,0.0077,0.0089,0.0084,0.0099,0.0104,0.0109,0.008,0.0033,0.0642,0.5048,0.7302,0.8702,1,0.8107,0.4396,0.1355,0.0224,0.2355,0.3057,0.2853,0.3066,0.6858,1,1,1,1,0.8901,0.5764,0.2873,0.2003,0.1604,0.1956,0.3389,0.5707,0.4748,0.4969,0.3756,0.3242,0.3005,0.3038,0.3968,0.4095,0.354,0.3362,0.3035,0.372,0.5635,0.7634,0.8537,0.7715,0.7136,0.8108,0.6955,0.6036,0.4479,0.3035,0.0683,0.1532,0.3781,0.5799,0.6103,0.3608,0.3477,0.4844,0.5064,0.4801,0.3623,0.1955,0.0436,0.1377,0.0833,0.0779,0.0812,0.7548,0.9791,0.7436,0.4135,0.5236,0.6207,0.5031,0.5121,0.2668,0.2516,0.396,0.2576,0.0987,0.0897,0.1683,0.1185,0.0831,0.029,0.0385,0.0347,0.0279,0.0068,0.003,0.0018,0.0014,0.0004,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,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.652790367603302}};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>