Foxlum commited on
Commit
f11c761
·
verified ·
1 Parent(s): 797f395

Deploy from HuggingChat

Browse files
Files changed (2) hide show
  1. README.md +9 -5
  2. index.html +606 -18
README.md CHANGED
@@ -1,10 +1,14 @@
1
  ---
2
- title: Pixelbender A Fake C64 Demoscene Prod
3
- emoji: 🏃
4
- colorFrom: gray
5
- colorTo: purple
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
1
  ---
2
+ title: "PIXELBENDER a fake C64 demoscene prod"
3
+ emoji: 🎯
4
+ colorFrom: purple
5
+ colorTo: indigo
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - huggingchat
10
  ---
11
 
12
+ # PIXELBENDER a fake C64 demoscene prod
13
+
14
+ Built with [HuggingChat](https://huggingface.co/chat).
index.html CHANGED
@@ -1,19 +1,607 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>PIXELBENDER C64 demoscene prod</title>
7
+ <style>
8
+ html,body{margin:0;padding:0;height:100%;background:#000;overflow:hidden;}
9
+ body{display:flex;align-items:center;justify-content:center;}
10
+ #wrap{position:relative;line-height:0;}
11
+ canvas{image-rendering:pixelated;image-rendering:crisp-edges;}
12
+ #scan{
13
+ position:absolute;inset:0;pointer-events:none;
14
+ background:repeating-linear-gradient(to bottom,
15
+ rgba(0,0,0,.28) 0, rgba(0,0,0,.28) 0.379%,
16
+ transparent 0.379%, transparent 0.758%);
17
+ }
18
+ #start{
19
+ position:fixed;inset:0;z-index:10;background:#706DEB;
20
+ display:flex;align-items:center;justify-content:center;cursor:pointer;user-select:none;
21
+ }
22
+ #start .scr{
23
+ background:#2E2C9B;color:#706DEB;font-family:"Courier New",monospace;
24
+ width:84%;height:80%;padding:24px 32px;font-size:clamp(11px,2.2vh,18px);line-height:1.6;
25
+ }
26
+ #start pre{margin:0;white-space:pre-wrap;}
27
+ .blink{animation:blink 1s steps(2) infinite;color:#EDF171;}
28
+ @keyframes blink{50%{opacity:0;}}
29
+ </style>
30
+ </head>
31
+ <body>
32
+ <div id="wrap">
33
+ <canvas id="scr" width="384" height="264"></canvas>
34
+ <div id="scan"></div>
35
+ </div>
36
+ <div id="start">
37
+ <div class="scr"><pre> **** COMMODORE 64 BASIC V2 ****
38
+
39
+ 64K RAM SYSTEM 38911 BASIC BYTES FREE
40
+
41
+ READY.
42
+ LOAD"PIXELBENDER",8,1
43
+
44
+ <span class="blink">** CLICK TO BOOT **</span></pre></div>
45
+ </div>
46
+
47
+ <script>
48
+ "use strict";
49
+ /* ===================================================================
50
+ PIXELBENDER — strict C64 constraints:
51
+ 320x200 / 40x25 chars (MC char mode for plasma: 4x8 cells),
52
+ fixed 16-color VIC-II palette, 8 sprites 24x21 (1 color, X/Y expand),
53
+ 3 SID voices (tri/saw/pulse/noise, ADSR, one shared filter),
54
+ 50 Hz PAL frame rate, music player on the "raster IRQ".
55
+ =================================================================== */
56
+
57
+ const PAL=[[0,0,0],[255,255,255],[129,51,56],[117,206,200],[142,60,151],[86,172,77],
58
+ [46,44,155],[237,241,113],[142,80,41],[85,56,0],[198,117,114],[74,74,74],
59
+ [123,123,123],[169,255,159],[112,109,235],[178,178,178]];
60
+ const PAL32=PAL.map(c=>0xFF000000|(c[2]<<16)|(c[1]<<8)|c[0]);
61
+
62
+ const FBW=384,FBH=264;
63
+ const dcanvas=document.getElementById('scr'),dctx=dcanvas.getContext('2d');
64
+ const IMG=dctx.createImageData(FBW,FBH),FB32=new Uint32Array(IMG.data.buffer);
65
+
66
+ /* VIC-II "memory" */
67
+ const SCREEN=new Uint8Array(1000),COLRAM=new Uint8Array(1000);
68
+ const lineBG=new Uint8Array(200),lineBorder=new Uint8Array(264),lineMC=new Uint8Array(200);
69
+ let MULTICOLOR=false,MC1=0,MC2=0;
70
+ function clearScreen(){SCREEN.fill(0);COLRAM.fill(1);}
71
+ function resetLines(bg,bo){lineBG.fill(bg);lineBorder.fill(bo);lineMC.fill(MULTICOLOR?1:0);}
72
+
73
+ /* charset: 256 chars x 8 bytes */
74
+ const CS=new Uint8Array(256*8);
75
+ function installGlyph(code,rows){
76
+ for(let r=0;r<8;r++){let b=0;const s=rows[r];
77
+ for(let x=0;x<8;x++)if(s[x]==='#')b|=0x80>>x;
78
+ CS[code*8+r]=b;}
79
+ }
80
+ /* chars 1..17: multicolor dither densities (pair 01 = color RAM) */
81
+ const B4=[[0,8,2,10],[12,4,14,6],[3,11,1,9],[15,7,13,5]];
82
+ for(let L=0;L<=16;L++)for(let y=0;y<8;y++){
83
+ let b=0;for(let p=0;p<4;p++)if(B4[y&3][p]<L)b|=1<<(6-2*p);
84
+ CS[(1+L)*8+y]=b;
85
+ }
86
+ /* chars 18..33: star chars, 2x2 dot at 4x4 sub-positions */
87
+ for(let i=0;i<16;i++){
88
+ const dx=(i&3)*2,dy=(i>>2)*2;
89
+ for(let r=0;r<2;r++)CS[(18+i)*8+dy+r]=(0xC0>>dx)&0xFF;
90
+ }
91
+ for(let r=0;r<8;r++)CS[34*8+r]=0xFF; /* block cursor */
92
+
93
+ /* 8x8 font at chars 64+ */
94
+ const FONT_SRC={
95
+ ' ':["........","........","........","........","........","........","........","........"],
96
+ '!':["...##...","...##...","...##...","...##...","...##...","........","...##...","........"],
97
+ '"':["..##.##.","..##.##.","..##.##.","........","........","........","........","........"],
98
+ "'":["...##...","...##...","..##....","........","........","........","........","........"],
99
+ '(':["....##..","...##...","..##....","..##....","..##....","...##...","....##..","........"],
100
+ ')':["..##....","...##...","....##..","....##..","....##..","...##...","..##....","........"],
101
+ '*':["........","..#..#..","...##...",".######.","...##...","..#..#..","........","........"],
102
+ '+':["........","...##...","...##...",".######.","...##...","...##...","........","........"],
103
+ ',':["........","........","........","........","...##...","...##...","..##....","........"],
104
+ '-':["........","........","........",".######.","........","........","........","........"],
105
+ '.':["........","........","........","........","........","...##...","...##...","........"],
106
+ ':':["........","...##...","...##...","........","...##...","...##...","........","........"],
107
+ '/':["......##",".....##.","....##..","...##...","..##....",".##.....","##......","........"],
108
+ '=':["........","........",".######.","........",".######.","........","........","........"],
109
+ '?':[".#####..","##...##.",".....##.","....##..","...##...","........","...##...","........"],
110
+ '0':[".#####..","##...##.","##..###.","##.####.","####.##.","###..##.",".#####..","........"],
111
+ '1':["...##...","..###...","...##...","...##...","...##...","...##...",".######.","........"],
112
+ '2':[".#####..","##...##.",".....##.","...###..","..##....",".##.....","#######.","........"],
113
+ '3':[".#####..","##...##.",".....##.","..####..",".....##.","##...##.",".#####..","........"],
114
+ '4':["....##..","...###..","..####..",".##.##..","#######.","....##..","....##..","........"],
115
+ '5':["#######.","##......","######..",".....##.",".....##.","##...##.",".#####..","........"],
116
+ '6':[".#####..","##...##.","##......","######..","##...##.","##...##.",".#####..","........"],
117
+ '7':["#######.",".....##.","....##..","...##...","..##....","..##....","..##....","........"],
118
+ '8':[".#####..","##...##.","##...##.",".#####..","##...##.","##...##.",".#####..","........"],
119
+ '9':[".#####..","##...##.","##...##.",".######.",".....##.","##...##.",".#####..","........"],
120
+ 'A':["..###...",".##.##..","##...##.","##...##.","#######.","##...##.","##...##.","........"],
121
+ 'B':["######..","##...##.","##...##.","######..","##...##.","##...##.","######..","........"],
122
+ 'C':[".#####..","##...##.","##......","##......","##......","##...##.",".#####..","........"],
123
+ 'D':["######..","##...##.","##...##.","##...##.","##...##.","##...##.","######..","........"],
124
+ 'E':["#######.","##......","##......","######..","##......","##......","#######.","........"],
125
+ 'F':["#######.","##......","##......","######..","##......","##......","##......","........"],
126
+ 'G':[".#####..","##...##.","##......","##.####.","##...##.","##...##.",".#####..","........"],
127
+ 'H':["##...##.","##...##.","##...##.","#######.","##...##.","##...##.","##...##.","........"],
128
+ 'I':[".######.","...##...","...##...","...##...","...##...","...##...",".######.","........"],
129
+ 'J':["...####.","....##..","....##..","....##..","....##..","##..##..",".####...","........"],
130
+ 'K':["##...##.","##..##..","##.##...","####....","##.##...","##..##..","##...##.","........"],
131
+ 'L':["##......","##......","##......","##......","##......","##......","#######.","........"],
132
+ 'M':["##...##.","###.###.","#######.","##.#.##.","##...##.","##...##.","##...##.","........"],
133
+ 'N':["##...##.","###..##.","####.##.","##.####.","##..###.","##...##.","##...##.","........"],
134
+ 'O':[".#####..","##...##.","##...##.","##...##.","##...##.","##...##.",".#####..","........"],
135
+ 'P':["######..","##...##.","##...##.","######..","##......","##......","##......","........"],
136
+ 'Q':[".#####..","##...##.","##...##.","##...##.","##.####.","##..##..",".####.#.","........"],
137
+ 'R':["######..","##...##.","##...##.","######..","##.##...","##..##..","##...##.","........"],
138
+ 'S':[".#####..","##...##.","##......",".#####..",".....##.","##...##.",".#####..","........"],
139
+ 'T':["########","...##...","...##...","...##...","...##...","...##...","...##...","........"],
140
+ 'U':["##...##.","##...##.","##...##.","##...##.","##...##.","##...##.",".#####..","........"],
141
+ 'V':["##...##.","##...##.","##...##.","##...##.","##...##.",".##.##..","..###...","........"],
142
+ 'W':["##...##.","##...##.","##...##.","##.#.##.","#######.","###.###.","##...##.","........"],
143
+ 'X':["##...##.","##...##.",".##.##..","..###...",".##.##..","##...##.","##...##.","........"],
144
+ 'Y':["##...##.","##...##.",".##.##..","..###...","...##...","...##...","...##...","........"],
145
+ 'Z':["#######.",".....##.","....##..","...##...","..##....",".##.....","#######.","........"]
146
+ };
147
+ const GL={};let fc=64;
148
+ for(const k in FONT_SRC){GL[k]=fc;installGlyph(fc,FONT_SRC[k]);fc++;}
149
+
150
+ /* text helpers */
151
+ const CYCLE=[1,7,10,2,8,4,12,15,3,14,6,11,11,6,14,3,15,12,4,8,2,10,7,1];
152
+ function text(row,col,str,color){
153
+ for(let i=0;i<str.length;i++){
154
+ const cell=row*40+col+i;if(cell<0||cell>=1000)continue;
155
+ const g=GL[str[i]];SCREEN[cell]=g===undefined?GL[' ']:g;COLRAM[cell]=color;
156
+ }
157
+ }
158
+ function cycText(row,col,str,off){
159
+ for(let i=0;i<str.length;i++){
160
+ const cell=row*40+col+i;if(cell<0||cell>=1000)continue;
161
+ const g=GL[str[i]];SCREEN[cell]=g===undefined?GL[' ']:g;
162
+ COLRAM[cell]=CYCLE[(i+(off>>2))%24];
163
+ }
164
+ }
165
+
166
+ /* raster bars */
167
+ function expand(r,n){const G=[];for(const c of r)for(let i=0;i<n;i++)G.push(c);return G;}
168
+ const RB=[6,14,3,1,1,3,14,6],RR=[2,10,7,1,1,7,10,2],RG=[5,13,3,1,1,3,13,5],
169
+ RP=[4,12,3,1,1,3,12,4],RY=[8,10,7,1,1,7,10,8],RW=[11,12,15,1,1,15,12,11];
170
+ const EB2=[RB,RR,RG,RP,RY,RW].map(r=>expand(r,2));
171
+ const EB4=[RB,RR,RG,RP,RY,RW].map(r=>expand(r,4));
172
+ function hbar(cy,G){
173
+ const n=G.length,y0=Math.round(cy-n/2);
174
+ for(let i=0;i<n;i++){const y=y0+i;if(y>=0&&y<200){lineBG[y]=G[i];lineBorder[y+32]=G[i];}}
175
+ }
176
+ function bbar(cy,G){
177
+ const n=G.length,y0=Math.round(cy-n/2);
178
+ for(let i=0;i<n;i++){const y=y0+i;if(y>=0&&y<264)lineBorder[y]=G[i];}
179
+ }
180
+
181
+ /* sprites: 8 x 24x21, 1 color, X/Y expand, priority 0>7 */
182
+ const sprites=[];
183
+ for(let i=0;i<8;i++)sprites.push({on:false,data:null,x:0,y:0,color:1,xexp:false,yexp:false});
184
+ const BALL=new Uint8Array(63);
185
+ for(let y=0;y<21;y++)for(let x=0;x<24;x++){
186
+ const dx=x-11.5,dy=y-10;
187
+ if(dx*dx+dy*dy<=100){
188
+ if(y>14&&((x+y)&1))continue;
189
+ BALL[y*3+(x>>3)]|=0x80>>(x&7);
190
+ }
191
+ }
192
+ for(let y=4;y<9;y++)for(let x=5;x<10;x++){
193
+ const dx=x-7,dy=y-6;if(dx*dx+dy*dy<=4)BALL[y*3+(x>>3)]&=~(0x80>>(x&7));
194
+ }
195
+ const LOGO=[];for(let i=0;i<8;i++)LOGO.push(new Uint8Array(63));
196
+ (function(){
197
+ const txt="PIXELBENDER",bmp=new Uint8Array(192*21);
198
+ for(let i=0;i<txt.length;i++){
199
+ const g=GL[txt[i]];
200
+ for(let r=0;r<7;r++){const b=CS[g*8+r];
201
+ for(let p=0;p<8;p++)if(b&(0x80>>p))bmp[(6+r)*192+i*8+p+52]=1;}
202
+ }
203
+ for(let s=0;s<8;s++)for(let r=0;r<21;r++)for(let b=0;b<3;b++){
204
+ let by=0;
205
+ for(let p=0;p<8;p++)if(bmp[r*192+s*24+b*8+p])by|=0x80>>p;
206
+ LOGO[s][r*3+b]=by;
207
+ }
208
+ })();
209
+ function drawSprites(){
210
+ for(let s=7;s>=0;s--){
211
+ const S=sprites[s];if(!S.on||!S.data)continue;
212
+ const col=PAL32[S.color&15],ex=S.xexp?2:1,ey=S.yexp?2:1;
213
+ const x0=S.x|0,y0=S.y|0;
214
+ for(let r=0;r<21;r++)for(let b=0;b<3;b++){
215
+ const by=S.data[r*3+b];if(!by)continue;
216
+ for(let p=0;p<8;p++){
217
+ if(!(by&(0x80>>p)))continue;
218
+ const px=x0+(b*8+p)*ex,py=y0+r*ey;
219
+ for(let dy=0;dy<ey;dy++){const y=py+dy;if(y<0||y>263)continue;
220
+ const o=y*FBW;
221
+ for(let dx=0;dx<ex;dx++){const x=px+dx;if(x>=0&&x<384)FB32[o+x]=col;}
222
+ }
223
+ }
224
+ }
225
+ }
226
+ }
227
+
228
+ /* DYCP scrolltext */
229
+ const SCROLL="HELLO AND WELCOME TO PIXELBENDER *** A NEW PROD BY NULLPOINTER CREW FOR THE COMMODORE 64 *** 64 KILOBYTES OF RAM ... A 1 MEGAHERTZ 6510 ... 16 FIXED COLOURS ... 3 SID VOICES ... 40 BY 25 CHARACTERS ... AND NOT ONE BYTE MORE *** EVERYTHING HERE OBEYS THE REAL HARDWARE: CHARACTER PLASMA ... CHARSET STARFIELD ... DYCP SCROLLTEXT ... 8 HARDWARE SPRITES AT 24 BY 21 PIXELS ... AND RASTER BARS TIMED TO THE SCANLINE *** THE MUSIC IS A 3 VOICE SID PLAYER TICKING ON THE 50 HZ RASTER INTERRUPT *** GREETINGS TO EVERY SCENER STILL MAKING THE SILICON SING *** SPACE SKIPS A PART ... M MUTES ... S TOGGLES SCANLINES *** REMEMBER: THE SCENE IS NOT DEAD ... IT JUST FITS IN 64K *** WRAPPING AROUND ......... ";
230
+ let scrollPos=0,dycpOn=false;
231
+ function drawDYCP(){
232
+ if(!dycpOn)return;
233
+ const len=SCROLL.length,ci=scrollPos>>3,fx=scrollPos&7;
234
+ for(let c=-1;c<=40;c++){
235
+ let idx=(ci+c)%len;if(idx<0)idx+=len;
236
+ const g=GL[SCROLL[idx]];
237
+ if(g===undefined||g===GL[' '])continue;
238
+ const x0=32+c*8-fx;
239
+ const y0=190+Math.round(18*Math.sin((ci+c)*0.4+frame*0.04));
240
+ const col=PAL32[CYCLE[(((ci+c)+(frame>>2))%24+24)%24]];
241
+ for(let r=0;r<7;r++){
242
+ const by=CS[g*8+r];if(!by)continue;
243
+ const y=y0+r;if(y<32||y>231)continue;
244
+ const o=y*FBW;
245
+ for(let p=0;p<8;p++)if(by&(0x80>>p)){const x=x0+p;if(x>=32&&x<352)FB32[o+x]=col;}
246
+ }
247
+ }
248
+ }
249
+
250
+ /* starfield state */
251
+ const stars=[];
252
+ for(let i=0;i<70;i++)stars.push({x:Math.random()*320,y:24+Math.random()*166,sp:(i%2)?1.7:0.8,fast:!!(i%2)});
253
+
254
+ /* parts */
255
+ const PRAMP=[6,6,14,14,3,3,13,13,5,5,7,7,1,1,7,10,10,2,2,4,4,12,12,15,15,1,1,15,12,11,6,6];
256
+ const GREETS=["VECTOR ANONYMOUS","THE MIDNIGHT CODERS","SILICON SYNDICATE","MEGABLASTERS INC","CTRL-ALT-DEFEAT","THE COFFEE MACHINE","ALL C64 SCENERS","AND YOU DEAR VIEWER"];
257
+ const BOOT_LEN=230;
258
+ let frame=0,lastPart=0;
259
+
260
+ function partBoot(pf){
261
+ resetLines(6,14);clearScreen();
262
+ const C=14;let cx=0,cy=5;
263
+ if(pf>10)text(1,0," **** COMMODORE 64 BASIC V2 ****",C);
264
+ if(pf>30)text(3,0," 64K RAM SYSTEM 38911 BASIC BYTES FREE",C);
265
+ if(pf>50){text(5,0,"READY.",C);cx=6;cy=5;}
266
+ if(pf>60){
267
+ const s='LOAD"PIXELBENDER",8,1';
268
+ const n=Math.min(s.length,(pf-60)>>1);
269
+ text(7,0,s.slice(0,n),C);cx=n;cy=7;
270
+ }
271
+ if(pf>140){text(9,0,"SEARCHING FOR PIXELBENDER",C);cx=25;cy=9;}
272
+ if(pf>170){text(11,0,"LOADING",C);cx=7;cy=11;}
273
+ if(pf>200){text(13,0,"READY.",C);cx=6;cy=13;}
274
+ if(pf>215){text(15,0,"RUN",C);cx=3;cy=15;}
275
+ if((pf>>3)&1){const cell=cy*40+cx;if(cell<1000){SCREEN[cell]=34;COLRAM[cell]=C;}}
276
+ if(pf>=224)lineBorder.fill([1,15,12,11,0,0][pf-224]||0);
277
+ }
278
+ function part1(pf){ /* rasters + sprite logo */
279
+ resetLines(0,0);clearScreen();
280
+ hbar(100+Math.round(85*Math.sin(frame*0.021)),EB2[0]);
281
+ hbar(100+Math.round(85*Math.sin(frame*0.017+2.1)),EB2[1]);
282
+ hbar(100+Math.round(85*Math.sin(frame*0.013+4.2)),EB2[2]);
283
+ bbar(16+9*Math.sin(frame*0.045),EB2[5]);
284
+ bbar(248+9*Math.sin(frame*0.045+1.5),EB2[5]);
285
+ cycText(1,7,"NULLPOINTER CREW PRESENTS",frame);
286
+ text(16,5,"CODE + GFX + MUSIC IN THE 50 HZ IRQ",15);
287
+ for(let i=0;i<8;i++){const S=sprites[i];
288
+ S.y=52+Math.round(12*Math.sin(frame*0.055+i*0.7));
289
+ S.color=CYCLE[(i*2+(frame>>3))%24];
290
+ }
291
+ }
292
+ function part2(pf){ /* multicolor char plasma */
293
+ resetLines(0,0);
294
+ for(let y=0;y<8;y++)lineMC[y]=0; // raster split: hi-res title row over MC screen
295
+ bbar(16+10*Math.sin(frame*0.035),EB2[0]);
296
+ bbar(248+10*Math.sin(frame*0.035+2.5),EB2[3]);
297
+ const tt=frame*0.05;
298
+ for(let r=0;r<25;r++)for(let c=0;c<40;c++){
299
+ const v=Math.sin(c*0.31+tt)+Math.sin(r*0.44-tt*1.15)
300
+ +Math.sin((c+r)*0.21+tt*0.55)
301
+ +Math.sin(Math.sqrt((c-20)*(c-20)+(r-12.5)*(r-12.5))*0.4-tt*1.35);
302
+ const d=(v+4)*0.125,cell=r*40+c;
303
+ SCREEN[cell]=1+Math.min(16,(d*17)|0);
304
+ COLRAM[cell]=PRAMP[(((d*28)|0)+(frame>>3))&31];
305
+ }
306
+ cycText(0,10,"PART 2: CHAR PLASMA",frame);
307
+ }
308
+ function part3(pf){ /* charset starfield + sprite sinus */
309
+ resetLines(0,0);clearScreen();
310
+ cycText(1,3,"PART 3: STARFIELD + SPRITE SINUS",frame);
311
+ cycText(2,6,"8 SPRITES, 24X21 PIXELS EACH",frame+12);
312
+ for(const st of stars){
313
+ st.x-=st.sp;if(st.x<0){st.x+=320;st.y=24+Math.random()*166;}
314
+ const col=st.x>>3,row=st.y>>3;if(row<3||row>24)continue;
315
+ const cell=row*40+col;
316
+ SCREEN[cell]=18+(((st.x|0)&7)>>1)+(((st.y|0)&7)>>1)*4;
317
+ COLRAM[cell]=st.fast?1:12;
318
+ }
319
+ for(let i=0;i<8;i++){const S=sprites[i];
320
+ S.x=192+170*Math.sin(frame*0.018+i*0.9)-12;
321
+ S.y=132+98*Math.sin(frame*0.023+i*1.7)-10;
322
+ S.color=CYCLE[(i*3+(frame>>3))%24];
323
+ }
324
+ bbar(16+8*Math.sin(frame*0.04),EB2[5]);
325
+ bbar(248+8*Math.sin(frame*0.04+2),EB2[5]);
326
+ }
327
+ function part4(pf){ /* fullscreen raster bars + greets */
328
+ resetLines(0,0);clearScreen();
329
+ for(let i=0;i<5;i++)
330
+ hbar(100+Math.round(135*Math.sin(frame*0.016*(1+i*0.35)+i*1.9)),EB4[i%5]);
331
+ text(3,9,"GREETINGS FLY OUT TO:",1);
332
+ const shown=Math.min(GREETS.length,1+((pf/90)|0));
333
+ for(let i=0;i<shown;i++){
334
+ const name=GREETS[i];
335
+ cycText(6+i*2,(40-name.length)>>1,name,frame+i*7);
336
+ }
337
+ }
338
+ const PARTSF=[part1,part2,part3,part4];
339
+
340
+ function initPart(p){
341
+ clearScreen();resetLines(0,0);
342
+ MULTICOLOR=(p===2);MC1=0;MC2=0;dycpOn=true;
343
+ for(const s of sprites)s.on=false;
344
+ if(p===1)for(let i=0;i<8;i++){const S=sprites[i];
345
+ S.on=true;S.data=LOGO[i];S.x=96+i*24;S.y=52;S.color=1;S.xexp=false;S.yexp=true;}
346
+ if(p===3)for(let i=0;i<8;i++){const S=sprites[i];
347
+ S.on=true;S.data=BALL;S.x=192;S.y=132;S.color=CYCLE[(i*3)%24];S.xexp=false;S.yexp=false;}
348
+ }
349
+
350
+ /* renderer: border + char screen + dycp + sprites */
351
+ function render(){
352
+ for(let y=0;y<264;y++){
353
+ const bc=PAL32[lineBorder[y]];
354
+ let o=y*FBW;
355
+ if(y<32||y>=232){for(let x=0;x<384;x++)FB32[o+x]=bc;continue;}
356
+ for(let x=0;x<32;x++){FB32[o+x]=bc;FB32[o+352+x]=bc;}
357
+ const sy=y-32,rr=sy>>3,ry=sy&7,bg=PAL32[lineBG[sy]];
358
+ o+=32;
359
+ if(lineMC[sy]){ // per-scanline video-mode split
360
+ const m1=PAL32[MC1],m2=PAL32[MC2];
361
+ for(let c=0;c<40;c++){
362
+ const cell=rr*40+c,byte=CS[SCREEN[cell]*8+ry],cc=PAL32[COLRAM[cell]&15];
363
+ for(let p=0;p<4;p++){
364
+ const v=(byte>>(6-2*p))&3;
365
+ const col=v===0?bg:v===1?cc:v===2?m1:m2;
366
+ FB32[o+c*8+p*2]=col;FB32[o+c*8+p*2+1]=col;
367
+ }
368
+ }
369
+ }else{
370
+ for(let c=0;c<40;c++){
371
+ const cell=rr*40+c,byte=CS[SCREEN[cell]*8+ry],cc=PAL32[COLRAM[cell]&15];
372
+ const oo=o+c*8;
373
+ FB32[oo ]=(byte&0x80)?cc:bg;FB32[oo+1]=(byte&0x40)?cc:bg;
374
+ FB32[oo+2]=(byte&0x20)?cc:bg;FB32[oo+3]=(byte&0x10)?cc:bg;
375
+ FB32[oo+4]=(byte&0x08)?cc:bg;FB32[oo+5]=(byte&0x04)?cc:bg;
376
+ FB32[oo+6]=(byte&0x02)?cc:bg;FB32[oo+7]=(byte&0x01)?cc:bg;
377
+ }
378
+ }
379
+ }
380
+ drawDYCP();
381
+ drawSprites();
382
+ dctx.putImageData(IMG,0,0);
383
+ }
384
+
385
+ /* ---------- SID: 3 voices in an AudioWorklet ---------- */
386
+ const SID_CODE=`
387
+ class SID extends AudioWorkletProcessor{
388
+ constructor(){
389
+ super();
390
+ this.ctlN=Math.max(1,Math.round(sampleRate/50));
391
+ this.ctl=0;this.v=[];
392
+ for(let i=0;i<3;i++)this.v.push({phase:0,freq:110,pw:0.5,wave:1,env:0,stage:0,
393
+ a:0.01,d:0.1,s:0.8,r:0.1,vol:0.8,lfsr:0x7ffff8});
394
+ this.cut=900;this.res=0.7;this.low=0;this.band=0;
395
+ this.route=[false,true,false];
396
+ this.dcx=0;this.dcy=0;
397
+ this.port.onmessage=(e)=>{
398
+ const m=e.data;
399
+ if(m.t==='v'){
400
+ const V=this.v[m.i],p=m.p;
401
+ for(const k in p)V[k]=p[k];
402
+ if(m.trig){V.env=0;V.stage=1;V.phase=0;V.lfsr=0x7ffff8;}
403
+ else if(m.gate===false&&V.stage!==0&&V.stage!==4)V.stage=4;
404
+ else if(m.gate===true&&V.stage===0)V.stage=1;
405
+ }else if(m.t==='f'){this.cut=m.cut;if(m.res!==undefined)this.res=m.res;}
406
+ };
407
+ }
408
+ tick(){
409
+ for(let i=0;i<3;i++){const V=this.v[i];
410
+ if(V.stage===1){V.env+=1/Math.max(1,V.a*50);if(V.env>=1){V.env=1;V.stage=2;}}
411
+ else if(V.stage===2){V.env-=1/Math.max(1,V.d*50);if(V.env<=V.s){V.env=V.s;V.stage=3;}}
412
+ else if(V.stage===4){V.env-=1/Math.max(1,V.r*50);if(V.env<=0){V.env=0;V.stage=0;}}
413
+ }
414
+ }
415
+ process(inputs,outputs){
416
+ const out=outputs[0][0];
417
+ const f=Math.min(0.9,2*Math.sin(Math.PI*Math.max(30,this.cut)/sampleRate));
418
+ const q=1.1-this.res*0.9;
419
+ for(let i=0;i<out.length;i++){
420
+ if(--this.ctl<=0){this.ctl=this.ctlN;this.tick();}
421
+ let fin=0,dry=0;
422
+ for(let vi=0;vi<3;vi++){
423
+ const V=this.v[vi];
424
+ V.phase+=V.freq/sampleRate;
425
+ if(V.phase>=1){
426
+ V.phase-=1;
427
+ const bit=((V.lfsr>>22)^(V.lfsr>>17))&1;
428
+ V.lfsr=((V.lfsr<<1)|bit)&0x7fffff;
429
+ if(V.lfsr===0)V.lfsr=0x7ffff8;
430
+ }
431
+ let s;
432
+ if(V.wave===0)s=V.phase<0.5?V.phase*4-1:3-V.phase*4;
433
+ else if(V.wave===1)s=V.phase*2-1;
434
+ else if(V.wave===2)s=V.phase<V.pw?1:-1;
435
+ else s=((V.lfsr>>20)&1)*2-1;
436
+ s*=V.env*V.vol;
437
+ if(this.route[vi])fin+=s;else dry+=s;
438
+ }
439
+ this.low+=f*this.band;
440
+ const high=fin-this.low-q*this.band;
441
+ this.band+=f*high;
442
+ const x=dry+this.low;
443
+ const y=x-this.dcx+0.995*this.dcy;
444
+ this.dcx=x;this.dcy=y;
445
+ out[i]=y*0.3;
446
+ }
447
+ return true;
448
+ }
449
+ }
450
+ registerProcessor('sid',SID);
451
+ `;
452
+ let actx=null,sidNode=null,sidGain=null,audioReady=false,muted=false;
453
+ async function initAudio(){
454
+ actx=new (window.AudioContext||window.webkitAudioContext)();
455
+ const url=URL.createObjectURL(new Blob([SID_CODE],{type:'application/javascript'}));
456
+ await actx.audioWorklet.addModule(url);
457
+ sidNode=new AudioWorkletNode(actx,'sid',{numberOfOutputs:1,outputChannelCount:[1]});
458
+ sidGain=actx.createGain();sidGain.gain.value=0.9;
459
+ sidNode.connect(sidGain);sidGain.connect(actx.destination);
460
+ if(actx.state==='suspended')await actx.resume();
461
+ audioReady=true;
462
+ }
463
+
464
+ /* ---------- 50 Hz interrupt music player ---------- */
465
+ const IN={
466
+ lead:{wave:2,pw:0.5,a:0.004,d:0.12,s:0.70,r:0.14,vol:0.75,vib:{depth:0.35,delay:12},pwm:{depth:0.27,rate:0.09}},
467
+ bass:{wave:1,a:0.003,d:0.09,s:0.75,r:0.08,vol:0.95},
468
+ arp:{wave:2,pw:0.5,a:0.002,d:0.06,s:0.30,r:0.05,vol:0.30},
469
+ kick:{wave:0,a:0.002,d:0.11,s:0.0,r:0.06,vol:1.0,penv:[26,19,13,9,6,4,2,1]},
470
+ snare:{wave:3,a:0.001,d:0.16,s:0.0,r:0.06,vol:0.80},
471
+ hat:{wave:3,a:0.001,d:0.025,s:0.0,r:0.02,vol:0.40}
472
+ };
473
+ const LEAD=[
474
+ 76,0,79,0,81,0,79,0,76,0,74,0,76,0,72,0,
475
+ 76,0,79,0,81,0,83,0,84,0,83,0,81,0,79,0,
476
+ 77,0,81,0,84,0,81,0,77,0,76,0,77,0,74,0,
477
+ 74,0,79,0,83,0,86,0,83,0,79,0,74,0,71,0];
478
+ const LEAD2=LEAD.concat(LEAD.map(n=>n?n+12:0));
479
+ const ROOTS=[33,33,29,31],BASSPAT=[0,0,12,0,0,0,12,0];
480
+ const CHORDS=[[69,72,76],[69,72,76],[65,69,72],[67,71,74]];
481
+ const SPEED=4;
482
+ const VS=[0,1,2].map(()=>({note:0,instr:null,age:0,trig:false,gate:false}));
483
+ let mframe=0,row=0,leadRow=0,rowFrame=0,arpIdx=0;
484
+ const mtof=m=>440*Math.pow(2,(m-69)/12);
485
+
486
+ function playRow(){
487
+ const bar=(row>>4)&3,r=row&15;
488
+ const n=LEAD2[leadRow];
489
+ if(n){const v=VS[0];v.note=n;v.instr=IN.lead;v.age=0;v.trig=true;v.gate=true;}
490
+ else VS[0].gate=false;
491
+ if((r&1)===0){const v=VS[1];
492
+ v.note=ROOTS[bar]+12+BASSPAT[(r>>1)&7];v.instr=IN.bass;v.age=0;v.trig=true;v.gate=true;}
493
+ const v=VS[2];
494
+ if(r===0||r===8||r===10){v.note=45;v.instr=IN.kick;}
495
+ else if(r===4||r===12){v.note=84;v.instr=IN.snare;}
496
+ else if(r===2||r===6||r===14){v.note=96;v.instr=IN.hat;}
497
+ else{const ch=CHORDS[bar];v.note=ch[arpIdx%3];arpIdx++;v.instr=IN.arp;}
498
+ v.age=0;v.trig=true;v.gate=true;
499
+ }
500
+ function musicTick(){
501
+ if(!audioReady)return;
502
+ mframe++;rowFrame++;
503
+ if(rowFrame>=SPEED){rowFrame=0;row=(row+1)&63;leadRow=(leadRow+1)&127;playRow();}
504
+ for(let i=0;i<3;i++){
505
+ const v=VS[i];if(!v.instr)continue;
506
+ v.age++;
507
+ const ins=v.instr;
508
+ let semis=0;
509
+ if(ins.penv&&v.age<ins.penv.length)semis=ins.penv[v.age];
510
+ if(ins.vib&&v.age>ins.vib.delay)semis+=ins.vib.depth*Math.sin(v.age*0.75);
511
+ let pw=ins.pw||0.5;
512
+ if(ins.pwm)pw=0.5+ins.pwm.depth*Math.sin(mframe*ins.pwm.rate);
513
+ sidNode.port.postMessage({t:'v',i:i,
514
+ p:{freq:mtof(v.note+semis),pw:pw,wave:ins.wave,vol:ins.vol,a:ins.a,d:ins.d,s:ins.s,r:ins.r},
515
+ trig:v.trig,gate:v.gate});
516
+ v.trig=false;
517
+ }
518
+ sidNode.port.postMessage({t:'f',cut:500+380*Math.sin(mframe*0.045),res:0.75});
519
+ }
520
+
521
+ /* ---------- 50 Hz master clock ---------- */
522
+ function tick(){
523
+ if(frame<BOOT_LEN){partBoot(frame);}
524
+ else{
525
+ const cf=(frame-BOOT_LEN)%3200;
526
+ const part=cf<800?1:cf<1600?2:cf<2400?3:4;
527
+ const pf=cf%800;
528
+ if(part!==lastPart){initPart(part);lastPart=part;}
529
+ PARTSF[part-1](pf);
530
+ if(pf<5)lineBorder.fill([1,15,12,11,0][pf]);
531
+ }
532
+ scrollPos=(scrollPos+2)%(SCROLL.length*8);
533
+ if(frame>=BOOT_LEN)musicTick();
534
+ frame++;
535
+ }
536
+ let started=false,lastT=0,acc=0;
537
+ function loop(ts){
538
+ requestAnimationFrame(loop);
539
+ if(!started)return;
540
+ if(!lastT)lastT=ts;
541
+ acc+=ts-lastT;lastT=ts;
542
+ if(acc>120)acc=120;
543
+ let n=0;
544
+ while(acc>=20&&n<5){acc-=20;tick();n++;}
545
+ if(n)render();
546
+ }
547
+ requestAnimationFrame(loop);
548
+
549
+ /* ---------- input / start / fit ---------- */
550
+ async function start(){
551
+ if(started)return;started=true;
552
+ document.getElementById('start').style.display='none';
553
+ try{await initAudio();}catch(e){console.warn('SID offline:',e);}
554
+ }
555
+ document.getElementById('start').addEventListener('click',start);
556
+ window.addEventListener('keydown',e=>{
557
+ if(!started){
558
+ if(e.code==='Space'||e.code==='Enter'){start();e.preventDefault();}
559
+ return;
560
+ }
561
+ if(e.code==='Space'){
562
+ if(frame<BOOT_LEN)frame=BOOT_LEN;
563
+ else{const cf=(frame-BOOT_LEN)%3200;frame+=800-(cf%800);}
564
+ e.preventDefault();
565
+ }
566
+ if(e.code==='KeyM'){muted=!muted;if(sidGain)sidGain.gain.value=muted?0:0.9;}
567
+ if(e.code==='KeyS'){
568
+ const sc=document.getElementById('scan');
569
+ sc.style.display=sc.style.display==='none'?'block':'none';
570
+ }
571
+ });
572
+ function fit(){
573
+ const s=Math.min(window.innerWidth/384,window.innerHeight/264);
574
+ const sc=s>=2?Math.floor(s):s;
575
+ dcanvas.style.width=(384*sc)+'px';
576
+ dcanvas.style.height=(264*sc)+'px';
577
+ }
578
+ window.addEventListener('resize',fit);
579
+ fit();
580
+ </script>
581
+ </body>
582
  </html>
583
+ <script data-huggingchat-badge="script">
584
+ (function () {
585
+ try {
586
+ if (document.querySelector("[data-huggingchat-badge]:not(script)")) return;
587
+ var host = document.createElement("div");
588
+ host.setAttribute("data-huggingchat-badge", "");
589
+ var styles = [["all","initial"],["position","fixed"],["right","12px"],["bottom","12px"],["z-index","2147483647"],["display","block"],["visibility","visible"],["opacity","1"],["pointer-events","auto"],["width","auto"],["height","auto"],["margin","0"],["padding","0"],["max-width","none"],["max-height","none"],["transform","none"],["filter","none"],["clip-path","none"],["color-scheme","light"]];
590
+ for (var i = 0; i < styles.length; i++) {
591
+ host.style.setProperty(styles[i][0], styles[i][1], "important");
592
+ }
593
+ host.attachShadow({ mode: "closed" }).innerHTML = "<style>\n:host { all: initial; }\na {\n display: flex;\n align-items: center;\n gap: 5px;\n box-sizing: border-box;\n height: 26px;\n padding: 0 9px 0 7px;\n border-radius: 999px;\n border: 1px solid rgba(0, 0, 0, 0.07);\n background: rgba(255, 255, 255, 0.92);\n -webkit-backdrop-filter: saturate(180%) blur(8px);\n backdrop-filter: saturate(180%) blur(8px);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08), 0 6px 16px rgba(0, 0, 0, 0.06);\n color: #111827;\n font: 500 11px/1 ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", sans-serif;\n letter-spacing: 0.01em;\n text-decoration: none;\n white-space: nowrap;\n opacity: 0.72;\n transition: opacity 0.15s ease, transform 0.15s ease;\n}\na:hover, a:focus-visible { opacity: 1; transform: translateY(-1px); }\nsvg { width: 14px; height: 14px; flex: none; }\n@media print { a { display: none; } }\n<\/style><a href=\"https://huggingface.co/chat\" target=\"_blank\" rel=\"noopener noreferrer nofollow\"><svg viewBox=\"0 0 32 32\" fill=\"none\" aria-hidden=\"true\"><path d=\"M16.0006 25.9992C13.8266 25.999 11.7118 25.2901 9.97686 23.9799C8.2419 22.6698 6.98127 20.8298 6.38599 18.7388C5.79071 16.6478 5.89323 14.4198 6.678 12.3923C7.46278 10.3648 8.88705 8.64837 10.735 7.50308C12.5829 6.35779 14.7538 5.84606 16.9187 6.04544C19.0837 6.24481 21.1246 7.14442 22.7323 8.60795C24.34 10.0715 25.4268 12.0192 25.8281 14.1559C26.2293 16.2926 25.9232 18.5019 24.9561 20.449C24.7703 20.8042 24.7223 21.2155 24.8211 21.604L25.4211 23.8316C25.4803 24.0518 25.4805 24.2837 25.4216 24.5039C25.3627 24.7242 25.2468 24.925 25.0856 25.0862C24.9244 25.2474 24.7235 25.3633 24.5033 25.4222C24.283 25.4811 24.0512 25.4809 23.831 25.4217L21.6034 24.8217C21.2172 24.7248 20.809 24.7729 20.4558 24.9567C19.0683 25.6467 17.5457 26.0068 16.0006 26.0068V25.9992Z\" fill=\"currentColor\"/><path d=\"M9.62598 16.0013C9.62598 15.3799 10.1294 14.8765 10.7508 14.8765C11.3721 14.8765 11.8756 15.3799 11.8756 16.0013C11.8756 17.0953 12.3102 18.1448 13.0838 18.9184C13.8574 19.692 14.9069 20.1266 16.001 20.1267C17.095 20.1267 18.1445 19.692 18.9181 18.9184C19.6918 18.1448 20.1264 17.0953 20.1264 16.0013C20.1264 15.3799 20.6299 14.8765 21.2512 14.8765C21.8725 14.8765 22.3759 15.3799 22.3759 16.0013C22.3759 17.6921 21.7046 19.3137 20.509 20.5093C19.3134 21.7049 17.6918 22.3762 16.001 22.3762C14.3102 22.3762 12.6885 21.7049 11.4929 20.5093C10.2974 19.3137 9.62598 17.6921 9.62598 16.0013Z\" fill=\"#fff\"/><\/svg><span>Made with HuggingChat<\/span><\/a>";
594
+ function attach() {
595
+ var parent = document.body || document.documentElement;
596
+ if (parent && host.parentNode !== parent) parent.appendChild(host);
597
+ }
598
+ attach();
599
+ // Artifacts that rewrite document.body wholesale would drop the badge.
600
+ if (document.body && typeof MutationObserver === "function") {
601
+ new MutationObserver(attach).observe(document.body, { childList: true });
602
+ } else {
603
+ document.addEventListener("DOMContentLoaded", attach);
604
+ }
605
+ } catch (e) {}
606
+ })();
607
+ </script>