PageSourceSearch

https://tenso.rs/demos/rock-paper-scissors/built/demo.js

js tenso.rs collected 2026-10-03 23:45:39 UTC 17,845 bytes, 491 lines download raw bytes

1'use strict';
2
3var videoLoop = function () {
4    var _ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee(t) {
5        var dt;
6        return regeneratorRuntime.wrap(function _callee$(_context) {
7            while (1) {
8                switch (_context.prev = _context.next) {
9                    case 0:
10                        dt = t - lastTime;
11                        _context.next = 3;
12                        return update(dt);
13
14                    case 3:
15
16                        lastTime = t;
17                        if (scrollY > innerHeight * 0.8) {
18                            setTimeout(videoLoop, 1000);
19                        } else {
20                            lastFrame = requestAnimationFrame(videoLoop);
21                        }
22
23                    case 5:
24                    case 'end':
25                        return _context.stop();
26                }
27            }
28        }, _callee, this);
29    }));
30
31    return function videoLoop(_x) {
32        return _ref.apply(this, arguments);
33    };
34}();
35
36var squeezenet = function () {
37    var _ref2 = _asyncToGenerator(regeneratorRuntime.mark(function _callee2() {
38        var keras_model, keras_model_meta, buffer, network, compiled;
39        return regeneratorRuntime.wrap(function _callee2$(_context2) {
40            while (1) {
41                switch (_context2.prev = _context2.next) {
42                    case 0:
43                        _context2.next = 2;
44                        return loadJSON('models/rps.json');
45
46                    case 2:
47                        keras_model = _context2.sent;
48                        _context2.next = 5;
49                        return loadJSON('models/rps_metadata.json');
50
51                    case 5:
52                        keras_model_meta = _context2.sent;
53                        _context2.next = 8;
54                        return loadBuffer('models/rps-weights.buf');
55
56                    case 8:
57                        buffer = _context2.sent;
58
59                        // var buffer = await loadBuffer('models/rps-0.9575_weights.buf');
60
61                        console.log(keras_model, keras_model_meta, buffer);
62
63                        // var image = (await loadArrayFromURL('data/cat-227x227x3')).transpose(1, 0, 2);
64
65                        // ;(new Tensor(gl, image)).show({ offset: 0.5, scale: 1/256, flipY: true })
66
67
68                        network = import_keras_network(keras_model, keras_model_meta, buffer);
69
70                        console.log(network);
71                        console.log(network.splice(-2, 2)); // cut off the last two layers, do that in JS
72
73                        _context2.next = 15;
74                        return compile(gl, network, { input_1: zeros([227, 227, 3]) });
75
76                    case 15:
77                        compiled = _context2.sent;
78
79                        C = compiled;
80
81                        // await destroy(gl, compiled)
82
83                    case 17:
84                    case 'end':
85                        return _context2.stop();
86                }
87            }
88        }, _callee2, this);
89    }));
90
91    return function squeezenet() {
92        return _ref2.apply(this, arguments);
93    };
94}();
95
96var update = function () {
97    var _ref3 = _asyncToGenerator(regeneratorRuntime.mark(function _callee3(dt) {
98        var compiled, videox, vwidth, im, data, results, imagedata, factor;
99        return regeneratorRuntime.wrap(function _callee3$(_context3) {
100            while (1) {
101                switch (_context3.prev = _context3.next) {
102                    case 0:
103                        // await model.ready()
104
105                        ctx.clearRect(0, 0, canvas.width, canvas.height);
106
107                        compiled = C;
108
109                        // var videox = window.game_state === 'playing'
110                        //     ? 0
111                        //     : canvas.width / 4
112
113                        videox = 0;
114
115
116                        if (hasVideo) {
117
118                            ctx.save();
119                            ctx.translate(canvas.width / 2, 0);
120                            ctx.scale(-1, 1);
121
122                            vwidth = canvas.height / video.videoHeight * video.videoWidth;
123
124                            ctx.drawImage(video, (video.videoWidth - video.videoHeight) / 2, 0, video.videoHeight, video.videoHeight, videox, 0, canvas.height, canvas.height);
125                            ctx.restore();
126                        }
127
128                        im = ctx.getImageData(videox, 0, 227, 227);
129                        _context3.next = 7;
130                        return run(gl, compiled, { input_1: preprocess_input(im) });
131
132                    case 7:
133                        data = compiled.info['global_average_pooling2d_1'].output.read().data;
134                        results = softmax(Array.from(data));
135                        imagedata = tickerCtx.getImageData(0, 0, ticker.width, ticker.height);
136
137                        tickerCtx.clearRect(0, 0, ticker.width, ticker.height);
138                        tickerCtx.putImageData(imagedata, -1, 0);
139
140                        factor = .6;
141
142                        results.map(function (x) {
143                            return Math.sqrt(x);
144                        }).forEach(function (r, i) {
145                            derps[i] = derps[i] * factor + r * (1 - factor);
146                            tickerCtx.fillStyle = colors[i];
147                            tickerCtx.fillRect(ticker.width - 1, Math.round(ticker.height / 4 * (i + .5 - derps[i] / 2 * 0.9)), 1, Math.round(ticker.height / 4 * (derps[i] * 0.9)));
148                        });
149
150                        person_move = results.indexOf(Math.max.apply(Math, _toConsumableArray(results)));
151                        // person_move = derps.indexOf(Math.max(...derps))
152
153                        avg_person_move = derps.indexOf(Math.max.apply(Math, derps));
154                        if (avg_person_move === 2) {
155                            // rock
156                            if (Date.now() - last_nonrock > 700) {
157                                // woot we're ready to partaaay
158                                window.game_state = 'playing';
159                            }
160                        } else {
161                            last_nonrock = Date.now();
162                        }
163
164                        ctx.font = '15px Arial';
165                        ctx.fillStyle = '#fff';
166                        ctx.textAlign = 'center';
167                        ctx.textBaseline = 'middle';
168                        ctx.fillText('YOU: ' + wins, videox + canvas.width / 4, 20);
169                        ctx.fillText('COMPUTER: ' + losses, videox + canvas.width * 3 / 4, 20);
170
171                        ctx.font = '40px Arial';
172                        ctx.fillStyle = '#fff';
173                        ctx.textAlign = 'center';
174                        ctx.textBaseline = 'middle';
175                        ctx.fillText('vs', videox + canvas.width / 2, canvas.height / 2);
176
177                        ctx.font = '30px Arial';
178                        ctx.fillStyle = '#fff';
179                        ctx.textAlign = 'right';
180                        ctx.textBaseline = 'bottom';
181                        ctx.fillText(MOVES[person_move], videox + canvas.width / 2 - 20, canvas.height - 15);
182
183                        // ctx.font = '100px Arial'
184                        // ctx.fillStyle = '#fff'
185                        // ctx.textAlign = 'center'
186                        // ctx.textBaseline = 'middle'
187                        // ctx.fillText(
188                        //     MOVES[person_move],
189                        //     videox + canvas.width/4 ,
190                        //     canvas.height / 2
191                        // )
192
193                        if (window.game_state === 'playing') {
194                            if (animations[cur_animation](dt)) {
195                                cur_animation++;
196                                if (cur_animation >= animations.length) {
197                                    cur_animation = 0;
198                                    window.game_state = 'waiting';
199                                }
200                                // cur_animation = (cur_animation + 1) % animations.length
201                            }
202                        } else {
203                            ctx.fillStyle = 'white';
204                            ctx.textAlign = 'center';
205                            ctx.textBaseline = 'middle';
206                            ctx.font = '20px Arial';
207                            ctx.fillText('to begin game', canvas.width / 4 * 3, canvas.height / 2 - 15);
208                            ctx.fillText('raise ✊ rock', canvas.width / 4 * 3, canvas.height / 2 + 15);
209                        }
210
211                    case 34:
212                    case 'end':
213                        return _context3.stop();
214                }
215            }
216        }, _callee3, this);
217    }));
218
219    return function update(_x2) {
220        return _ref3.apply(this, arguments);
221    };
222}();
223
224var gotVideo = function () {
225    var _ref4 = _asyncToGenerator(regeneratorRuntime.mark(function _callee4() {
226        return regeneratorRuntime.wrap(function _callee4$(_context4) {
227            while (1) {
228                switch (_context4.prev = _context4.next) {
229                    case 0:
230                        hasVideo = true;
231                        _context4.next = 3;
232                        return squeezenet();
233
234                    case 3:
235                        start();
236
237                    case 4:
238                    case 'end':
239                        return _context4.stop();
240                }
241            }
242        }, _callee4, this);
243    }));
244
245    return function gotVideo() {
246        return _ref4.apply(this, arguments);
247    };
248}();
249
250function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
251
252function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { tr
252y { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
253
254var gl = TF.createGL(document.getElementById('glCanvas')),
255    OutputTensor = TF.OutputTensor,
256    Tensor = TF.Tensor,
257    InPlaceTensor = TF.InPlaceTensor,
258    TP = function TP(s) {
259    return function (out, opt) {
260        return TF.Run(s.join(''), out, opt);
261    };
262};
263gl.canvas.width = 227;
264gl.canvas.height = 227;
265
266var canvas = document.getElementById('canvas'),
267    ctx = canvas.getContext('2d');
268
269// var img = new Image;
270// img.src = 'data/sims.png'
271// img.onload = function(){
272//     ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
273// }
274
275var lastFrame, lastTime;
276function start() {
277    lastTime = performance.now();
278    videoLoop(lastTime);
279}
280
281var animations = [];
282var cur_animation = 0;
283
284var register_animation = function register_animation(duration, cb) {
285    var left = duration;
286    animations.push(function (dt) {
287        left -= dt;
288        if (left <= 0) {
289            cb(1);
290            left = duration;
291            return true;
292        }
293        cb(1 - left / duration);
294    });
295};
296
297var flash = function flash(text, legend) {
298    return function (r) {
299        var d = r < .5 ? r * 2 : (1 - r) * 2;
300
301        // ctx.fillStyle = 'rgba(255,255,0,'+ d / 2 + .5 +')'
302        ctx.fillStyle = 'rgba(255,255,0,1)';
303        ctx.textAlign = 'center';
304        ctx.textBaseline = 'middle';
305        ctx.font = '100px Arial';
306
307        ctx.fillText(text, canvas.width / 4 * 3, canvas.height / 2 + d * 40 - 40);
308
309        if (r > .5 || legend) {
310            ctx.font = '20px Arial';
311            ctx.fillText(['', 'scissors', 'paper', 'rock'][text], canvas.width / 4 * 3, canvas.height / 2 + 50);
312        }
313    };
314};
315
316var MOVES = ['👶', '✋', '✊', '✌️'];
317var losers = ['none', 2, 3, 1];
318window.colors = ['#eee', '#48f', 'yellow', 'rgb(255, 123, 0)'];
319
320var computer_move = 0;
321var person_move = 0;
322
323// register_animation(800, flash('Ready...'))
324register_animation(800, flash(3));
325register_animation(800, flash(2));
326register_animation(800, flash(1));
327register_animation(400, function (r) {
328    return flash(1)(r / 2);
329});
330register_animation(0, function () {
331    computer_move = Math.floor(Math.random() * 3) + 1;
332
333    // console.log('computer_move', computer_move)
334    // ctx.textBaseline = 'middle'
335    // tickerCtx.textAlign = 'right'
336    // tickerCtx.fillText(MOVES[person_move], ticker.width, ticker.height / 4 * (person_move + .5) )
337
338});
339register_animation(1000, function (r) {
340    ctx.fillStyle = 'rgba(255,255,255,' + (1 - r) / 2 + ')';
341    ctx.fillRect(0, 0, canvas.width, canvas.height);
342
343    ctx.textAlign = 'center';
344    ctx.textBaseline = 'middle';
345    ctx.font = '100px Arial';
346    ctx.fillStyle = 'rgba(255,255,255,1)';
347    ctx.fillText(MOVES[computer_move], canvas.width / 4 * 3, canvas.height / 2);
348});
349
350var wins = 0,
351    losses = 0,
352    ties = 0;
353var human_move = void 0;
354
355register_animation(0, function () {
356    human_move = avg_person_move;
357
358    ctx.textAlign = 'center';
359    ctx.textBaseline = 'middle';
360    ctx.font = '100px Arial';
361    ctx.fillStyle = 'rgba(255,255,255,1)';
362    ctx.fillText(MOVES[computer_move], canvas.width / 4 * 3, canvas.height / 2);
363
364    // ctx.fillText(MOVES[human_move],canvas.width / 4 ,canvas.height / 2)
365    if (computer_move === losers[human_move]) {
366        wins++;
367        tickerCtx.fillStyle = 'rgba(0,255,0,0.5)';
368    } else if (computer_move != human_move) {
369        losses++;
370        tickerCtx.fillStyle = 'rgba(255,0,0,0.5)';
371    } else {
372        ties++;
373        tickerCtx.fillStyle = 'rgba(0,0,0,0.5)';
374    }
375
376    console.log('wins: ', wins, 'losses: ', losses, 'ties: ', ties);
377
378    tickerCtx.fillRect(ticker.width - 1, 0, 1, ticker.height);
379});
380
381register_animation(1500, function (r) {
382    ctx.textAlign = 'center';
383    ctx.textBaseline = 'middle';
384    ctx.font = '100px Arial';
385    ctx.fillStyle = 'rgba(255,255,255,' + (1 - r) + ')';
386    // ctx.fillStyle = '#fff'
387    ctx.fillText(MOVES[computer_move], canvas.width / 4 * 3, canvas.height / 2);
388
389    ctx.font = '70px Arial';
390    ctx.fillStyle = 'rgba(0,0,0,' + Math.min(0.5, 2 * r) + ')';
391    ctx.fillRect(0, (canvas.height - 120) / 2, canvas.width, 120);
392    ctx.fillStyle = 'white';
393    if (computer_move === losers[human_move]) {
394        ctx.fillText('You Win!', canvas.width / 2, canvas.height / 2 - 20);
395
396        ctx.font = '30px Arial';
397        ctx.fillText(MOVES[human_move] + '>
397' + MOVES[computer_move], canvas.width / 2, canvas.height / 2 + 40);
398    } else if (computer_move != human_move) {
399        ctx.fillText('You Lose!', canvas.width / 2, canvas.height / 2 - 20);
400
401        ctx.font = '30px Arial';
402        ctx.fillText(MOVES[human_move] + '<' + MOVES[computer_move], canvas.width / 2, canvas.height / 2 + 40);
403    } else {
404        ctx.fillText('Tie!', canvas.width / 2, canvas.height / 2 - 20);
405
406        ctx.font = '30px Arial';
407        ctx.fillText(MOVES[human_move] + '=' + MOVES[computer_move], canvas.width / 2, canvas.height / 2 + 40);
408    }
409});
410
411register_animation(1000, function (r) {});
412
413var C;
414var video = document.getElementById('videoElement');
415
416function softmax(arr) {
417    var maxVal = arr[0];
418    for (var i = 1; i < arr.length; i++) {
419        if (arr[i] > maxVal) maxVal = arr[i];
420    }var exp = arr.map(function (k) {
421        return Math.exp(k - maxVal);
422    }),
423        expSum = exp.reduce(function (a, b) {
424        return a + b;
425    });
426    return exp.map(function (k) {
427        return k / expSum;
428    });
429}
430
431function normalize(arr) {
432    var maxVal = arr[0];
433    for (var i = 1; i < arr.length; i++) {
434        if (arr[i] > maxVal) maxVal = arr[i];
435    }return arr.map(function (k) {
436        return k / maxVal;
437    });
438}
439
440var avg_person_move;
441
442function preprocess_input(im) {
443    // data processing
444    // see https://github.com/fchollet/keras/blob/master/keras/applications/imagenet_utils.py
445    var dataTensor = ndarray(new Float32Array(im.data), [im.width, im.height, 4]);
446    var dataProcessedTensor = ndarray(new Float32Array(im.width * im.height * 3), [im.width, im.height, 3]);
447    ndops.subseq(dataTensor.pick(null, null, 2), 103.939);
448    ndops.subseq(dataTensor.pick(null, null, 1), 116.779);
449    ndops.subseq(dataTensor.pick(null, null, 0), 123.68);
450    ndops.assign(dataProcessedTensor.pick(null, null, 0), dataTensor.pick(null, null, 2));
451    ndops.assign(dataProcessedTensor.pick(null, null, 1), dataTensor.pick(null, null, 1));
452    ndops.assign(dataProcessedTensor.pick(null, null, 2), dataTensor.pick(null, null, 0));
453    return dataProcessedTensor;
454}
455
456var derps = [0, 0, 0, 0];
457
458var tickerCtx = ticker.getContext('2d');
459
460ticker.width = 400;
461ticker.height = 200;
462
463var last_nonrock = 0;
464
465var hasVideo = false;
466
467function connectWebcam() {
468    document.querySelector('.demo').classList.remove('waiting');
469    document.querySelector('.demo').classList.add('training');
470
471    if (navigator.mediaDevices) {
472        navigator.mediaDevices.getUserMedia({ video: true }).then(function (mediaStream) {
473            video.srcObject = mediaStream;
474
475            gotVideo();
476        }).catch(function (err) {
477            console.log(err.name + ": " + err.message);
478        }); // always check for errors at the end.    
479    } else {
480        navigator.getUserMedia({ video: true }, function (stream) {
481            video.src = window.URL.createObjectURL(stream);
482            gotVideo();
483        }, function () {
484            console.log('error');
485        });
486    }
487}
488
489if (!(navigator.mediaDevices || navigator.getUserMedia)) {
490    document.body.classList.add('no-gum');
491}

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.