PageSourceSearch

https://znah.net/hexells/sonic.js

js znah.net collected 2026-10-03 20:52:10 UTC 4,177 bytes, 152 lines download raw bytes

1const defs = `
2  const float PI = 3.14159265359;
3  const vec2 wh = vec2(90.0, 50.0);
4  const float border = 15.0;
5
6  varying vec2 uv;
7  varying float playingNote;
8  varying float element;
9`;
10
11const vs_code = defs+`
12    uniform sampler2D sonicTex;
13    uniform vec2 viewSize;
14    uniform vec2 sonicPos;
15    uniform float progress;
16  
17    attribute vec4 vertex;
18
19    float getElement(vec4 v, float i) {
20      if (i<1.0) return v.x;
21      if (i<2.0) return v.y;
22      if (i<3.0) return v.z;
23      return v.w;
24    }
25
26    const float sonicTexW = 16.0*12.0/4.0;
27
28    float getChannel(float i, float ch) {
29      float x = (floor(i*3.0 + ch/4.0)+0.5) / sonicTexW;
30      vec4 v4 = texture2D(sonicTex, vec2(x, 0.0));
31      return getElement(v4, mod(ch, 4.0));
32    }
33
34    void main() {
35        vec2 pos = sonicPos;
36        element = vertex.z;
37
38        const float shift = -90.0;
39        
40        if (element == -2.0) {
41          uv = vertex.xy;
42          float r = 8.0*(viewSize.x+viewSize.y)/1000.0;
43          pos += uv*r;
44        } else if (element == -1.0) {
45          pos += vec2(0.0, shift);
46          uv = vertex.xy*(wh+border);
47          pos += uv;
48        } else {
49          pos += vec2(0.0, shift);
50          uv = vertex.xy;
51          float i = floor(vertex.z / 9.0);
52          float ch = mod(vertex.z, 9.0);
53          float v = getChannel(i, ch+3.0); // skip RGB
54          pos += vec2((ch+(i-7.5)/40.0)/4.0-1.0, 1.0-v*2.0) * wh;
55          pos += vertex.xy*vec2(5.0, 5.0);
56          playingNote = float(ch==progress);
57        }
58        gl_Position = vec4(pos/viewSize*2.0-1.0 , 0.0, 1.0);
59        gl_Position.y *= -1.0;
60      
61    }`;
62
63const fs_code =  'precision highp float;\n'+defs+`
64    uniform sampler2D sonicTex;
65    
66    void main() {
67      if (element == -2.0) {
68        float v =  smoothstep(1.0, 0.8, length(uv));
69        gl_FragColor = vec4(1.0, 1.0, 1.0, v*0.5);
70      } else if (element == -1.0) {
71        float v = smoothstep(border, 0.0, length(max(abs(uv)-wh, 0.0)));
72        gl_FragColor = vec4(0.0, 0.0, 0.0, v*0.5);
73      } else {
74        float v = smoothstep(1.0, 0.0, length(uv));
75        gl_FragColor = vec4(1.0, 1.0, 1.0, v*(0.1+playingNote*0.5));
76      }
77    }
78`;
79
80
81export class Sonic {
82    constructor(gl, ca) {
83      this.gl = gl;
84      this.ca = ca;
85      this.program = twgl.createProgramInfo(gl, [vs_code, fs_code]);
86
87      const voiceN = 16;
88
89      const pos = []; 
90      for (let i=-2; i<9*voiceN; ++i) {
91          pos.push(-1, -1, i, 1, -1, i, -1, 1, i, -1, 1, i, 1, -1, i, 1, 1, i);
92      }
93      this.quads = twgl.createBufferInfoFromArrays(gl, {vertex: pos});
94
95      this.state = null;
96      this._progress = -1; 
97      this.env = new Tone.AmplitudeEnvelope({
98        attack: 0.05,
99        decay: 0.05,
100        sustain: 0.5,
101        release: 0.2
102      }).toDestination();
103      this.osc = Array(voiceN).fill().map(
104        ()=>new Tone.Oscillator({type:'triangle'}).connect(this.env).start()
105      );
106      Tone.Transport.start();
107
108      this.seq = new Tone.Sequence((time, i) => {
109        if (!this.state || i>=9)
110          return;
111        this._progress = i;
112        for (let j=0; j<voiceN; ++j) {
113          const note = 220*Math.pow(2.0, this.state.buf[j*12+i+3]/128-1.0);
114          this.osc[j].frequency.setValueAtTime(note, time);
115        }
116        this.env.triggerAttackRelease(0.05, time, 0.8/voiceN);
117      }, [...Array(12-3+0).keys()], 1.0/8.0).start();
118
119    }
120
121    play(state) {
122      this.state = state;
123    }
124
125    stop() {
126      this.state = null;
127      this._progress = -1;
128    }
129
130    progress() {
131      return this._progress;
132    }
133
134    draw(viewSize) {
135        if (!this.state)
136            return;
137        const gl = this.gl;
138        gl.useProgram(this.program.program);
139        twgl.setUniforms(this.program, {
140          viewSize,
141          sonicTex: this.state.tex,
142          sonicPos:this.state.pos,
143          progress: this._progress,
144        });
145        twgl.setBuffersAndAttributes(gl, this.program, this.quads);
146        gl.enable(gl.BLEND)
147        gl.blendEquation(gl.FUNC_ADD);
148        gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
149        twgl.drawBufferInfo(gl, this.quads);
150        gl.disable(gl.BLEND)
151    }
152  };

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.