1+ // Writing effects:
2+ // Effects are written as a class with an 'update' method that is
3+ // called to render each frame.
4+ //
5+ // The class you write will be initialized with the backing display for you to manipulate:
6+ //
7+ // display:
8+ // an object that has a 'setPixel' method, and a 'flush' method,
9+ // it also has 'width' and 'height' attributes:
10+ //
11+ // setPixel: function(x: number, y: number, v: [number, number, number])
12+ // Use this to set the colour of a pixel on the screen
13+ // v is a 3-tuple of RGB values in the range 0-255
14+ //
15+ // flush: function()
16+ // Use this to flush the display buffer to the system.
17+ // Make sure to call this, otherwise you'll not see anything!
18+ //
19+ // width: number
20+ // height: number
21+ // The size of the display in pixels. 0,0 is the top left corner
22+ //
23+ //
24+
25+ return class MyEffect {
26+ constructor ( display ) {
27+ this . pupil = [ 0 , 0 , 0 ] ;
28+ this . iris = [ 0 , 127 , 255 ] ;
29+ this . sclera = [ 255 , 255 , 255 ] ;
30+
31+ this . display = display ;
32+ this . xoffset = Math . floor ( this . display . width / 2 ) ;
33+ this . yoffset = Math . floor ( this . display . height / 2 ) ;
34+
35+ this . lrad = Math . floor ( this . display . width / 4 ) ;
36+ this . srad = Math . floor ( this . lrad / 2 ) ;
37+ this . xmin = this . lrad - 10 ;
38+ this . xmax = this . lrad + 10 ;
39+
40+ this . blinking = false ;
41+ this . eyelid = 0 ;
42+ this . blinkMove = 4 ; // Tune for blink speed
43+ this . blinkLimit = 50 ; // Don't close eyelid all the way
44+ this . blinkProb = 0.005 ; // Blink if Math.random() < this value
45+
46+ this . count = 0 ; // Tune for gaze speed
47+
48+ this . gaze = this . xmin ;
49+ this . gazeMove = 1 ;
50+
51+ this . #clear( ) ;
52+ }
53+
54+ #clear( ) {
55+ for ( let x = 0 ; x < this . display . width ; x ++ ) {
56+ for ( let y = 0 ; y < this . display . height ; y ++ ) {
57+ this . display . setPixel ( x , y , [ 0 , 0 , 0 ] ) ;
58+ }
59+ }
60+
61+ this . display . flush ( ) ;
62+ }
63+
64+ //
65+ // Native function does no bounds checking
66+ //
67+ setPixel ( x , y , colour ) {
68+ if ( x < 0 || x >= this . display . width )
69+ return ;
70+ if ( y < 0 || y >= this . display . height )
71+ return ;
72+ this . display . setPixel ( x , y , colour ) ;
73+ }
74+
75+ circle ( rad , Xorig , Yorig , colour ) {
76+ for ( let y = - rad ; y <= rad ; y ++ ) {
77+ for ( let x = - rad ; x <= rad ; x ++ ) {
78+ if ( Yorig + y > this . eyelid && x * x + y * y <= rad * rad )
79+ this . setPixel ( Xorig + x , Yorig + y , colour ) ;
80+ }
81+ }
82+ }
83+
84+ eyeballs ( gaze ) {
85+ // left
86+ this . circle ( this . lrad , this . lrad , this . yoffset , this . sclera ) ;
87+ this . circle ( this . srad , gaze , this . yoffset + this . srad - 5 , this . iris ) ;
88+ this . circle ( this . srad - 4 , gaze , this . yoffset + this . srad - 5 , this . pupil ) ;
89+
90+ // right
91+ this . circle ( this . lrad , this . lrad + this . xoffset , this . yoffset , this . sclera ) ;
92+ this . circle ( this . srad , gaze + this . xoffset , this . yoffset + this . srad - 5 , this . iris ) ;
93+ this . circle ( this . srad - 4 , gaze + this . xoffset , this . yoffset + this . srad - 5 , this . pupil ) ;
94+ }
95+
96+ update ( ) {
97+
98+ this . #clear( ) ;
99+ this . eyeballs ( this . gaze )
100+ this . display . flush ( ) ;
101+
102+ if ( this . blinking == false && ( Math . random ( ) < this . blinkProb ) )
103+ this . blinking = true ;
104+ if ( this . blinking ) {
105+ this . eyelid = this . eyelid + this . blinkMove
106+ if ( this . eyelid <= 0 || this . eyelid >= this . blinkLimit )
107+ this . blinkMove = this . blinkMove * - 1 ;
108+ if ( this . eyelid < this . blinkMove )
109+ this . blinking = false ;
110+ }
111+
112+ this . count = ( this . count + 1 ) % 2 ;
113+ if ( this . count == 0 ) {
114+ this . gaze = this . gaze + this . gazeMove
115+ if ( this . gaze <= this . xmin || this . gaze >= this . xmax )
116+ this . gazeMove = this . gazeMove * - 1 ;
117+ }
118+ }
119+ }
0 commit comments