1/* 2 Mona â port of ilmenit's 250/256 byte Atari intro. 3 64 brush strokes of cycling color, driven by a 32-bit Galois LFSR. 4 Each stroke: length = (64 - i) * 32 pixels; LFSR picks axis/direction per pixel. 5 Seeds and algorithm match the original; see source notes in README. 6*/ 7(function () { 8 const BRUSH_PRE = 0x7EC8; 9 const BRUSH = [ 10 0x030A, 0x37BE, 0x2F9B, 0x072B, 0x0E3C, 0xF59B, 0x8A91, 0x1B0B, 11 0x0EBD, 0x9378, 0xB83E, 0xB05A, 0x70B5, 0x0280, 0xD0B1, 0x9CD2, 12 0x2093, 0x209C, 0x3D11, 0x26D6, 0xDF19, 0x97F5, 0x90A3, 0xA347, 13 0x8AF7, 0x0859, 0x29AD, 0xA32C, 0x7DFC, 0x0D7D, 0xD57A, 0x3051, 14 0xD431, 0x542B, 0xB242, 0xB114, 0x8A96, 0x2914, 0xB0F1, 0x532C, 15 0x0413, 0x0A09, 0x3EBB, 0xE916, 0x1877, 0xB8E2, 0xAC72, 0x80C7, 16 0x5240, 0x8D3C, 0x3EAF, 0xAD63, 0x1E14, 0xB23D, 0x238F, 0xC07B, 17 0xAF9D, 0x312E, 0x96CE, 0x25A7, 0x9E37, 0x2C44, 0x2BB9, 0x2139 18 ]; 19 20 // sepia/cream palette tuned to evoke the original intro 21 const PALETTE = [ 22 [0x14, 0x0C, 0x06, 0xFF], // 0 deep brown 23 [0xEA, 0xCB, 0x8A, 0xFF], // 1 cream 24 [0xC8, 0x8A, 0x55, 0xFF], // 2 sepia 25 [0x6C, 0x40, 0x22, 0xFF], // 3 dark sienna 26 ]; 27 28 class MonaEffect { 29 constructor(canvas) { 30 this.canvas = canvas; 31 this.ctx = canvas.getContext('2d', { alpha: false }); 32 this.W = 128; 33 this.H = 128; 34 this.canvas.width = this.W; 35 this.canvas.height = this.H; 36 this.img = this.ctx.createImageData(this.W, this.H); 37 this._raf = 0; 38 this._done = false; 39 } 40 41 reset() { 42 // fill with color 0 43 const d = this.img.data; 44 const [r, g, b, a] = PALETTE[0]; 45 for (let i = 0; i < d.length; i += 4) { 46 d[i] = r; d[i+1] = g; d[i+2] = b; d[i+3] = a; 47 } 48 // LFSR state 49 this.d6 = ((BRUSH_PRE << 16) >>> 0); 50 this.strokeIdx = 0; 51 this.pixelsInStroke = 0; 52 this.strokeLen = 0; 53 this.col = 0; 54 this.currentOp = 0x5245; // default: ADDQ.W #1, D5 (y+=1) 55 this._beginStroke(); 56 this._done = false; 57 } 58 59 _beginStroke() { 60 if (this.strokeIdx >= BRUSH.length) { this._done = true; return; } 61 // replace low word of d6 with next brush seed (keep high word from previous LFSR state) 62 this.d6 = ((this.d6 & 0xFFFF0000) | BRUSH[this.strokeIdx]) >>> 0; 63 // initial coords from low word of d6 64 this.d4 = this.d6 & 0xFF; // x seed 65 this.d5 = (this.d6 >>> 8) & 0xFF; // y seed 66 this.col = (this.col + 1) & 3; 67 this.strokeLen = (64 - this.strokeIdx) * 32; 68 this.pixelsInStroke = 0; 69 } 70 71 _step() { 72 // one LFSR+putpixel iteration 73 const carry = (this.d6 & 0x80000000) !== 0; 74 this.d6 = ((this.d6 << 1) >>> 0); 75 if (carry) { 76 this.d6 = (this.d6 ^ 0x04C11DB7) >>> 0; 77 // patch SMC: choose axis (bit 1) and direction (bit 7) 78 let op = 0x5245; 79 if ((this.d6 & 0x02) !== 0) op -= 1; // axis: 0 -> D5 (y), 1 -> D4 (x) 80 if ((this.d6 & 0x80) !== 0) op |= 0x0100; // direction: 0 -> +, 1 -> - 81 this.currentOp = op; 82 } 83 // execute current op: add/sub 1 to x or y 84 const delta = (this.currentOp & 0x0100) ? -1 : 1; 85 if ((this.currentOp & 1) === 1) this.d5 = (this.d5 + delta) & 0xFFFF; 86 else this.d4 = (this.d4 + delta) & 0xFFFF; 87 // mask to 128x128 88 const x = this.d4 & 0x7F; 89 const y = this.d5 & 0x7F; 90 // putpix 91 const idx = (y * this.W + x) * 4; 92 const c = PALETTE[this.col]; 93 this.img.data[idx] = c[0]; 94 this.img.data[idx + 1] = c[1]; 95 this.img.data[idx + 2] = c[2]; 96 this.img.data[idx + 3] = c[3]; 97 } 98 99 start() { 100 this.reset(); 101 const frame = () => { 102 // budget per frame so painting takes a few seconds 103 const perFrame = 480; 104 for (let i = 0; i < perFrame; i++) { 105 if (this._done) break; 106 this._step(); 107 this.pixelsInStroke++; 108 if (this.pixelsInStroke >= this.strokeLen) { 109 this.strokeIdx++; 110 this._beginStroke(); 111 } 112 } 113 this.ctx.putImageData(this.img, 0, 0); 114 if (this._done) { 115 // finished; hold the final frame 116 return; 117 } 118 this._raf = requestAnimationFrame(frame); 119 }; 120 this._raf = requestAnimationFrame(frame); 121 } 122 123 stop() { 124 if (this._raf) cancelAnimationFrame(this._raf); 125 this._raf = 0; 126 } 127 } 128 129 window.MonaEffect = MonaEffect; 130})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.