Inside
SELVEDGE.
A digital textile archive exploring the mathematics of woven cloth. The signature technique is canvas jacquard weaving.
Think in crossings
The canvas is a grid of seven-pixel thread crossings. Each cell chooses whether the copper weft passes above or below the indigo warp. This binary rule creates the fabric.
Change the weave, not the palette
Twill uses (x + y) mod 4 < 2. Diamond uses Manhattan distance in a repeating 24-cell tile. Plain weave alternates with (x + y) mod 2. Native buttons switch rules and announce the selection.
Add fiber-scale light
A deterministic sine hash varies highlight opacity. Horizontal and vertical one-pixel highlights follow whichever thread is on top. A one-pixel gap provides the darker interstice.
Keep it still
There is no continuous animation. ResizeObserver redraws the canvas after layout changes and pixel ratio is capped at two. The page pairs a quiet serif editorial column with the dense woven field.
Reproduce the site
- Download the complete source below and keep its name
index.html. - Open it through a static server. From its directory, run
python3 -m http.server 8000, then visithttp://localhost:8000. There is no install or build step. - The stylesheet lives in the head. The rendering and interaction code lives in the final script. Adjust one parameter at a time and compare the result at desktop and 375 px widths.
- Google Fonts requires a network connection. All visible artwork is computed locally. Native system fallbacks preserve readable text if fonts fail.
- Enable your operating system’s reduced-motion setting and repeat the interaction. Use browser developer tools to check errors, overflow, and frame timing before publishing.
The complete rendering and interaction code
const c=document.querySelector('#weave'),ctx=c.getContext('2d');let pattern=0;function draw(){const r=c.getBoundingClientRect(),d=Math.min(devicePixelRatio,2);c.width=r.width*d;c.height=r.height*d;ctx.scale(d,d);const size=7;ctx.fillStyle='#352b42';ctx.fillRect(0,0,r.width,r.height);for(let y=0;y<r.height/size;y++)for(let x=0;x<r.width/size;x++){const on=pattern===0?(x+y)%4<2:pattern===1?(Math.abs(x%24-12)+Math.abs(y%24-12))%8<4:(x+y)%2===0;const n=Math.sin(x*127.1+y*311.7)*43758.5;ctx.fillStyle=on?'#bf7444':'#3b344f';ctx.fillRect(x*size,y*size,size-1,size-1);ctx.fillStyle=on?'#dda676':'#696078';ctx.globalAlpha=.3+(n-Math.floor(n))*.4;ctx.fillRect(x*size+1,y*size+1,on?size-2:1,on?1:size-2);ctx.globalAlpha=1} }new ResizeObserver(draw).observe(c);document.querySelectorAll('[data-weave]').forEach(b=>b.onclick=()=>{pattern=+b.dataset.weave;document.querySelectorAll('[data-weave]').forEach(x=>x.setAttribute('aria-pressed',String(x===b)));document.querySelector('#weave-name').textContent=['01 / Broken twill · indigo & copper','02 / Diamond weave · indigo & copper','03 / Plain weave · indigo & copper'][pattern];draw()})The complete visual system
:root{--body:'Alegreya Sans',sans-serif}body{background:#eee2cb;color:#302239;font-family:var(--body)}header{padding:25px 4vw;display:flex;justify-content:space-between;align-items:center;border-bottom:1px solid #302239}header b{font:500 30px 'Alegreya';letter-spacing:.08em}header a{font-size:15px}.layout{display:grid;grid-template-columns:36% 64%;min-height:85svh}.text{padding:55px 4vw;display:flex;flex-direction:column;justify-content:space-between}.text h1{font:400 clamp(55px,5.6vw,90px)/.97 'Alegreya';letter-spacing:-.04em;margin:25px 0}.text p{font-size:19px;max-width:31ch}.text small{font-size:13px;letter-spacing:.12em}.cloth{position:relative;min-height:650px;background:#302239;padding:40px 35px;display:flex;align-items:center}.cloth canvas{width:100%;height:100%;position:absolute;inset:0}.swatch-label{position:absolute;bottom:30px;right:25px;background:#eee2cb;padding:18px 25px;box-shadow:2px 5px 10px #0003;font-size:13px;transform:rotate(-3deg)}.buttons{display:flex;flex-wrap:wrap;gap:10px;margin:25px 0}button{border:1px solid;background:transparent;padding:12px 16px;font-size:15px}button[aria-pressed=true]{background:#302239;color:#eee2cb}.archive{border-top:1px solid;padding:45px 4vw;display:grid;grid-template-columns:repeat(3,1fr);gap:40px}.archive h2{font:400 30px 'Alegreya';margin:0}.archive p{font-size:17px}.colophon{border-top:1px solid}@media(max-width:650px){.layout{grid-template-columns:1fr}.text{padding:30px 6vw}.text h1{font-size:64px}.cloth{min-height:480px;order:2}.archive{grid-template-columns:1fr;gap:18px}.archive p{margin-top:5px}header b{font-size:23px}}Original fictional design study. No stock photography, model files, image textures, or sample audio.