← back to Scarlet Riverboat Masquerade
Cutout/South-Park art pass: flat-color fills + bold dark outlines on the animal cast (fox/heron/rabbit/bear/cubs) + construction-paper grain overlay. Dancers stay silhouettes per the brief
18429744f4b3d3038540db3d413df8188c268894 · 2026-09-01 18:40:51 -0700 · Steve Abrams
Files touched
Diff
commit 18429744f4b3d3038540db3d413df8188c268894
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue Sep 1 18:40:51 2026 -0700
Cutout/South-Park art pass: flat-color fills + bold dark outlines on the animal cast (fox/heron/rabbit/bear/cubs) + construction-paper grain overlay. Dancers stay silhouettes per the brief
---
index.html | 54 ++++++++++++++++++++++++++++++++----------------------
1 file changed, 32 insertions(+), 22 deletions(-)
diff --git a/index.html b/index.html
index 9773e2f..19a1b72 100644
--- a/index.html
+++ b/index.html
@@ -23,6 +23,9 @@
html,body{margin:0;height:100%;background:#0a0406;color:var(--ink);font-family:var(--sans);overflow:hidden}
#stage{position:fixed;inset:0;display:block}
canvas{position:absolute;inset:0;width:100%;height:100%}
+ /* construction-paper grain — the cutout/South-Park texture over the whole scene */
+ .grain{position:fixed;inset:0;pointer-events:none;z-index:5;opacity:.10;mix-blend-mode:overlay;
+ background-image:url("data:image/svg+xml,%3Csvg viewBox='0 0 220 220' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E")}
/* ---- Skip link ---- */
.skip{
@@ -202,6 +205,7 @@
<canvas id="sky" aria-hidden="true"></canvas>
<canvas id="scene" aria-hidden="true"></canvas>
+ <div class="grain" aria-hidden="true"></div>
<div class="titleplate">
<p class="kicker">A Riverboat Listening Room</p>
@@ -388,6 +392,11 @@ function pal(key,i){ // blended palette between integer chapters
// Choreography snaps to the nearest chapter (palette blends, steps change crisply)
function CH(){ return CHAPTERS[clamp(Math.round(S.chapterMix),0,2)].choreo; }
+// cutout ("South Park") styling: flat fill + a bold dark outline, round joins
+const OUTLINE='rgba(8,3,5,0.92)';
+function cut(fill,w){ ctx.fillStyle=fill; ctx.strokeStyle=OUTLINE; ctx.lineWidth=w||2.4; ctx.lineJoin='round'; ctx.lineCap='round'; }
+function fillOutline(){ ctx.fill(); ctx.stroke(); } // paint the current path as a cutout shape
+
// ---------- Resize ----------
function resize(){
S.DPR = Math.min(2, window.devicePixelRatio||1);
@@ -773,10 +782,10 @@ function fox(x,y,s,t,accent){
const gait=Math.sin(t*3.2);
const bob=Math.abs(Math.sin(t*3.2))*-4;
ctx.save(); ctx.translate(x + Math.sin(t*0.4)*30, y+bob); ctx.scale(s,s);
- ctx.fillStyle='rgba(30,8,12,0.95)';
- ctx.beginPath(); ctx.ellipse(0,-10,15,7,0,0,7); ctx.fill();
- ctx.beginPath(); ctx.moveTo(12,-14); ctx.lineTo(24,-16); ctx.lineTo(13,-8); ctx.closePath(); ctx.fill();
- ctx.beginPath(); ctx.moveTo(14,-18); ctx.lineTo(17,-24); ctx.lineTo(19,-17);ctx.closePath(); ctx.fill();
+ cut('rgba(150,58,42,0.98)');
+ ctx.beginPath(); ctx.ellipse(0,-10,15,7,0,0,7); fillOutline();
+ ctx.beginPath(); ctx.moveTo(12,-14); ctx.lineTo(24,-16); ctx.lineTo(13,-8); ctx.closePath(); fillOutline();
+ ctx.beginPath(); ctx.moveTo(14,-18); ctx.lineTo(17,-24); ctx.lineTo(19,-17);ctx.closePath(); fillOutline();
ctx.strokeStyle=accent; ctx.lineWidth=5; ctx.lineCap='round'; ctx.globalAlpha=0.8;
ctx.beginPath(); ctx.moveTo(-14,-10); ctx.quadraticCurveTo(-26,-14-gait*5,-30,-4-gait*3); ctx.stroke();
ctx.globalAlpha=1;
@@ -801,9 +810,10 @@ function heron(x,y,s,t,accent){
ctx.moveTo(-2,-18); ctx.lineTo(-4,2+ (step>0?0:step*8));
ctx.moveTo(3,-18); ctx.lineTo(5,2+ (step>0?step*8:0));
ctx.stroke();
- ctx.beginPath(); ctx.ellipse(0,-24,10,7,-0.2,0,7); ctx.fill();
+ cut('rgba(96,44,66,0.98)');
+ ctx.beginPath(); ctx.ellipse(0,-24,10,7,-0.2,0,7); fillOutline();
ctx.save(); ctx.translate(0,-26); ctx.rotate(wing*0.6);
- ctx.beginPath(); ctx.moveTo(0,0); ctx.quadraticCurveTo(-14,-4,-20,4); ctx.quadraticCurveTo(-10,2,0,3); ctx.closePath(); ctx.fill();
+ ctx.beginPath(); ctx.moveTo(0,0); ctx.quadraticCurveTo(-14,-4,-20,4); ctx.quadraticCurveTo(-10,2,0,3); ctx.closePath(); fillOutline();
ctx.restore();
ctx.lineWidth=2.4;
ctx.beginPath(); ctx.moveTo(6,-28);
@@ -821,9 +831,9 @@ function rabbit(x,y,s,t,accent){
ctx.save();
ctx.translate(x + ((t*22)% (S.W*0.14)) - S.W*0.05, y+hopY);
ctx.scale(s*(2-stretch)*0.9 + s*0.1, s*stretch*squash);
- ctx.fillStyle='rgba(34,10,14,0.95)';
- ctx.beginPath(); ctx.ellipse(0,-8,9,7,0,0,7); ctx.fill();
- ctx.beginPath(); ctx.arc(8,-12,4.5,0,7); ctx.fill();
+ cut('rgba(120,66,60,0.98)',2);
+ ctx.beginPath(); ctx.ellipse(0,-8,9,7,0,0,7); fillOutline();
+ ctx.beginPath(); ctx.arc(8,-12,4.5,0,7); fillOutline();
const ear = air*0.8;
ctx.save(); ctx.translate(8,-15); ctx.rotate(ear);
ctx.beginPath(); ctx.ellipse(0,-6,2,6,0.2,0,7); ctx.fill();
@@ -839,13 +849,13 @@ function rabbit(x,y,s,t,accent){
function bearAndFireflies(x,y,s,t,accent){
const wob=Math.sin(t*0.35)*3;
ctx.save(); ctx.translate(x+wob,y); ctx.scale(s,s);
- ctx.fillStyle='rgba(20,6,10,0.97)';
- ctx.beginPath(); ctx.ellipse(0,-16,18,20,0,0,7); ctx.fill();
- ctx.beginPath(); ctx.ellipse(-14,-2,10,8,0,0,7); ctx.fill();
- ctx.beginPath(); ctx.ellipse(14,-2,10,8,0,0,7); ctx.fill();
- ctx.beginPath(); ctx.arc(0,-36,11,0,7); ctx.fill();
- ctx.beginPath(); ctx.arc(-9,-45,4,0,7); ctx.fill();
- ctx.beginPath(); ctx.arc(9,-45,4,0,7); ctx.fill();
+ cut('rgba(74,34,34,0.98)',2.8);
+ ctx.beginPath(); ctx.ellipse(0,-16,18,20,0,0,7); fillOutline();
+ ctx.beginPath(); ctx.ellipse(-14,-2,10,8,0,0,7); fillOutline();
+ ctx.beginPath(); ctx.ellipse(14,-2,10,8,0,0,7); fillOutline();
+ ctx.beginPath(); ctx.arc(0,-36,11,0,7); fillOutline();
+ ctx.beginPath(); ctx.arc(-9,-45,4,0,7); fillOutline();
+ ctx.beginPath(); ctx.arc(9,-45,4,0,7); fillOutline();
ctx.strokeStyle='rgba(20,6,10,0.97)'; ctx.lineWidth=6; ctx.lineCap='round';
const ax=Math.sin(t*0.7)*16, ay=-Math.abs(Math.cos(t*0.7))*10;
ctx.beginPath(); ctx.moveTo(6,-24); ctx.quadraticCurveTo(16,-34,18+ax,-40+ay); ctx.stroke();
@@ -875,12 +885,12 @@ function bearCub(x,y,s,t,ph){
const bounce=Math.abs(Math.sin(t*2.4+ph))*(2+beat*4+amp*2.5); // a grounded bop — feet stay near the shore
const rock=Math.sin(t*2.4+ph)*0.2;
ctx.save(); ctx.translate(x,y-bounce); ctx.rotate(rock); ctx.scale(s,s);
- ctx.fillStyle='rgba(24,7,11,0.96)';
- ctx.beginPath(); ctx.ellipse(0,-8,7,9,0,0,7); ctx.fill(); // body
- ctx.beginPath(); ctx.arc(0,-20,6,0,7); ctx.fill(); // head
- ctx.beginPath(); ctx.arc(-5,-25,2.5,0,7); ctx.fill(); // ears
- ctx.beginPath(); ctx.arc(5,-25,2.5,0,7); ctx.fill();
- ctx.strokeStyle='rgba(24,7,11,0.96)'; ctx.lineWidth=3; ctx.lineCap='round';
+ cut('rgba(88,42,40,0.98)',2);
+ ctx.beginPath(); ctx.ellipse(0,-8,7,9,0,0,7); fillOutline(); // body
+ ctx.beginPath(); ctx.arc(0,-20,6,0,7); fillOutline(); // head
+ ctx.beginPath(); ctx.arc(-5,-25,2.5,0,7); fillOutline(); // ears
+ ctx.beginPath(); ctx.arc(5,-25,2.5,0,7); fillOutline();
+ ctx.strokeStyle=OUTLINE; ctx.lineWidth=3; ctx.lineCap='round';
const sw=Math.sin(t*4.8+ph)*3; // arms wave in the dance
ctx.beginPath();
ctx.moveTo(-5,-11); ctx.lineTo(-10+sw,-17);
← 1043c03 Keep animals/cubs grounded (lower hop/bounce); add pterodact
·
back to Scarlet Riverboat Masquerade
·
Full cutout pass: flat-fill + bold outlines on dancers (with 9486ed3 →