g | x | w | all
Bytes Lang Time Link
229C gcc250805T071531ZCaydendW
7568MATL250627T203334ZLuis Men
236C gcc250630T165023Zjdt
086Uiua250704T223722ZErikDaPa
nanCP1610 machine code250627T172433ZArnauld
227PowerShell Core 7.4+250628T000031Zuser3141
322Java on Windows250630T115258ZKevin Cr
nanCommodore 64 Assembler250630T111126ZJani Joe
246JS250629T131050Zb00t
432HTML + CSS + JS250629T082636ZM. Zhang
293ZX Spectrum Basic250627T213317ZNeil
1024HTML + CSS + JS250627T173537ZA.L

C (gcc), 229 bytes

Contains unprintable characters:

*a=L"ð0@@!!!!!!\"DÂC0ð";*b=L"ð0@@!!!!!!\"DÂC0ð";*c;main(i){puts("\e[2J\e[H");for (i=0;i<256;++i%16||puts(""))putchar(a[i/8]&1<<(i%8)?42:32);c=a;a=b;b=c;usleep(1<<19);main();}

Stores the smiley faces in unprintable characters as a bitmap. Other things to possible optimise include: Reuse common parts of the strings and make the swapping better.

I'd add a TIO link but TIO doesn't like the infinite loop.

od dump of the program:

0000000 060452 046075 141442 007660 030014 040002 040002 141041
0000020 020600 100302 141041 020600 117702 141041 020600 100302
0000040 141001 000600 100302 021134 141504 041602 030014 130303
0000060 021017 025073 036542 021114 130303 006017 001060 001100
0000100 020500 102302 141041 020604 102302 141041 020604 102302
0000120 141041 000604 100302 141001 056200 042042 101303 006103
0000140 141460 007660 035442 061452 066473 064541 024156 024551
0000160 070173 072165 024163 056042 055545 045062 062534 044133
0000200 024442 063073 071157 024040 036551 035460 036151 032462
0000220 035466 025453 022551 033061 076174 072560 071564 021050
0000240 024442 070051 072165 064143 071141 060450 064533 034057
0000260 023135 036061 024074 022551 024470 032077 035062 031063
0000300 035451 036543 035541 036541 035542 036542 035543 071565
0000320 062554 070145 030450 036074 034461 035451 060555 067151
0000340 024450 076473 000012
0000345

MATL, 75 68 bytes

`TF'#c8Dw;Lv/P/7KMbi('F8:ZaY"8etPv!F85:90(!@o?tP*}F5:'x'+(]!1YG.5Y.T

You can try it at MATL online!, but it's slow. Here's an animated GIF from the offline compiler showing the actual speed:


enter image description here

C (gcc), 295 272 237 236 bytes

-23 bytes thanks to @Kevin Cruijssen!

-36 bytes thanks to @ceilingcat!

Try it online!

a[]: An array of 16 integers representing two 16×16 monochrome images.

f(b): A recursive function that:

Uiua, 86 bytes

&gifs2⊟∩⌟⬚0+∩⌝↘4_10⍉¤⊂1⟜(7_8¤)⋯31⬚0+⌝↘4_5⋯[.....1 .0 33 30]⊂⟜⇌≡(⊂⟜⇌)⋯[⊸×20⊸×6.˙+....1]

Result: enter image description here

Explanation:

&gifs2⊟∩⌟⬚0+∩⌝↘4_10⍉¤⊂1⟜(7_8¤)⋯31⬚0+⌝↘4_5⋯[.....1 .0 33 30]⊂⟜⇌≡(⊂⟜⇌)⋯[⊸×20⊸×6.˙+....1]
                                                            ⊂⟜⇌≡(⊂⟜⇌)⋯[⊸×20⊸×6.˙+....1] => create bitmap of the head outline using its top-left quadrant
                                                                                            (use of symmetry)
                                  ⬚0+⌝↘4_5⋯[.....1 .0 33 30]                             => bitmap for the non-blinking eye and mouth
            ∩⌝↘4_10⍉¤⊂1⟜(7_8¤)⋯31                                                        => bitmap for the blinking eye (two frames)
      ⊟∩⌟⬚0+                                                                             => combine all the components together for each frame
&gifs2                                                                                   => finally generate gif (2 frames/sec)

Try it online!

CP-1610 machine code, 89 DECLEs1=111.25 bytes

1. CP-1610 instructions are encoded with 10-bit values (0x000 to 0x3FF), known as DECLEs. Although the Intellivision is also able to work on 16-bit data, programs were really stored in 10-bit ROM back then.

This is a full program mapped at $4800-$4858. To get the correct delay of 500ms, this should be run on a 60Hz (NTSC) Intellivision.

We could save 1 DECLE by just incrementing the counter and testing bit #5. The frame change would occur every 32 frames (533.33ms).

Source code

                        ROMW  10                ; use 10-bit ROM
                        ORG   $4800             ; map our program at $4800

                  BT    EQU   $0200             ; BACKTAB address
                  RTN   EQU   $1014             ; return address of the ISR
                  CLEAR EQU   $1738             ; clear routine of the EXEC
                  GRAM  EQU   $3800             ; GRAM address

                        ;; --------------------------------------------------------- ;;
                        ;;  main code                                                ;;
                        ;; --------------------------------------------------------- ;;
4800  2B8 00A           MVII  #isr AND $FF, R0  ; set up the interrupt service routine
4802  240 100           MVO   R0, $100
4804  2B8 048           MVII  #isr SHR 8, R0
4806  240 101           MVO   R0, $101

4808  002               EIS                     ; enable interrupts

4809  017               DECR  R7                ; spin forever

                        ;; --------------------------------------------------------- ;;
                        ;;  ISR                                                      ;;
                        ;; --------------------------------------------------------- ;;
                        ;;  NB: called with R1 = $4800 (because of the way the EXEC  ;;
                        ;;  jumps to the entry point)                                ;;
                        ;; --------------------------------------------------------- ;;
                  isr   PROC

480A  2B8 034           MVII  #52, R0           ; clear the STIC registers
480C  1E4               CLRR  R4
480D  004 114 338       CALL  CLEAR

4810  0FC               ADDR  R7, R4            ; we now have R4 = 52, which is the
                                                ; distance from here to the tiles data
4811  001               SDBD                    ; R5 = pointer into GRAM
4812  2BD 000 038       MVII  #GRAM, R5

4815  2A0         @loop MVI@  R4, R0            ; R0 = tile data
4816  268         @copy MVO@  R0, R5            ; write the byte to the GRAM
4817  338 100           SUBI  #$100, R0         ; decrement the upper nibble
4819  223 004           BPL   @copy             ; copy again if still positive

481B  2F9 2CD           ADDI  #717, R1          ; this will make R1 negative-looking
481D  223 009           BPL   @loop             ; after 20 iterations ($4800 -> $8004)

481F  2BA 301           MVII  #$301, R2         ; R2 = address of 16-bit counter
4821  291               MVI@  R2, R1            ; R1 = said counter
4822  001 2F9 044       ADDI  #1092, R1         ; add 1092 (approximately 2**16 / 60)
4826  251               MVO@  R1, R2            ; save the updated value
4827  203 008           BPL   @draw             ; if it's negative-looking ...

4829  33D 00D           SUBI  #13, R5           ; ... adjust the GRAM pointer
                                                ; to update the right eye
482B  26A               MVO@  R2, R5            ; \
482C  26A               MVO@  R2, R5            ;  }- right border (R2 = %[..]00000001)
482D  26A               MVO@  R2, R5            ; /
482E  268               MVO@  R0, R5            ; --- horizontal line + right border
                                                ;     (R0 = %[..]11111001)
482F  26A               MVO@  R2, R5            ; \__ right border
4830  26A               MVO@  R2, R5            ; /

4831  2B9 008     @draw MVII  #8, R1            ; draw the tiles
4833  001               SDBD
4834  2B8 007 008       MVII  #$807, R0         ; bottom-left
4837  240 281           MVO   R0, BT+129
4839  0C8               ADDR  R1, R0            ; top-left
483A  240 26D           MVO   R0, BT+109
483C  0C8               ADDR  R1, R0            ; top-right
483D  240 26E           MVO   R0, BT+110
483F  0C8               ADDR  R1, R0            ; bottom-right
4840  240 282           MVO   R0, BT+130

4842  004 310 014       J     RTN               ; return from ISR

                        ENDP

                        ;; --------------------------------------------------------- ;;
                        ;;  tiles data in RBB format:                                ;;
                        ;;    R = number of times to repeat                          ;;
                        ;;    BB = byte to write                                     ;;
                        ;; --------------------------------------------------------- ;;
                  tiles PROC
                        ; tile #0: bottom-left quarter
4845  184               DECLE (1 SHL 8) OR %10000100
4846  180               DECLE (1 SHL 8) OR %10000000
4847  044               DECLE (0 SHL 8) OR %01000100
4848  043               DECLE (0 SHL 8) OR %01000011
4849  030               DECLE (0 SHL 8) OR %00110000
484A  10F               DECLE (1 SHL 8) OR %00001111  ; + 1st row of next tile

                        ; tile #1: top-left quarter
484B  030               DECLE (0 SHL 8) OR %00110000
484C  140               DECLE (1 SHL 8) OR %01000000
484D  384               DECLE (3 SHL 8) OR %10000100

                        ; tile #2: top-right quarter
484E  0F0               DECLE (0 SHL 8) OR %11110000
484F  00C               DECLE (0 SHL 8) OR %00001100
4850  102               DECLE (1 SHL 8) OR %00000010
4851  321               DECLE (3 SHL 8) OR %00100001

                        ; tile #3: bottom-right quarter
4852  121               DECLE (1 SHL 8) OR %00100001
4853  101               DECLE (1 SHL 8) OR %00000001
4854  022               DECLE (0 SHL 8) OR %00100010
4855  0C2               DECLE (0 SHL 8) OR %11000010
4856  00C               DECLE (0 SHL 8) OR %00001100
4857  0F0               DECLE (0 SHL 8) OR %11110000

                        ; pattern for the horizontal eye
4858  0F9               DECLE (0 SHL 8) OR %11111001
                        ENDP

Output

output

PowerShell Core 7.4+, 277 252 227 bytes

for(cls){-split("ff0 300c 4002 4002 $(((,8421*6),"$(,8401*3) 84f9 8401 8401")[++$w%2]) 8001 8001 4422 43c2 300c ff0")|%{1*"0x$_"|% *g b16|% T*y|%{' '|Write-Host -B(15*"$_")-N};''}
sleep -m 500
[Console]::SetCursorPosition(0,0)}

Another -25 bytes off in PowerShell Core 7.4+
.NET 8 introduced the 'b' format specifier, so the expensive
[Convert]::ToString("0x$_",2)|% *ft 16 '0'
can be replaced with
1*"0x$_"|% *g b16
Ungolfed:
[Convert]::ToString("0x$_", 2) | ForEach-Object -Member PadLeft 16 '0'
1*"0x$_" | ForEach-Object -Member ToString b16

PowerShell Windows and Core, 277 252 bytes

for(cls){-split("ff0 300c 4002 4002 $(((,8421*6),"$(,8401*3) 84f9 8401 8401")[++$w%2]) 8001 8001 4422 43c2 300c ff0")|%{[Convert]::ToString("0x$_",2)|% *ft 16 '0'|% T*y|%{' '|Write-Host -B(15*"$_")-N};''}
sleep -m 500
[Console]::SetCursorPosition(0,0)}

-25 bytes thanks to @Julian, who managed to squeeze the bit patterns even further.
One more byte could theoretically be shaved off by using light gray ((7*"$_")) (or any other single-digit color) instead of white ((15*"$_")), but who's counting when it comes to optics.


TIO doesn't support colors (or [console]::SetCursorPosition), so this needs to be saved as script, or pasted into a console. Sorry for the inconvenience.
Note when pasting into a PS console: do NOT use a right-click to paste, use Ctrl-V.
There's a bug in the PSReadline module where a right-click paste inserts the lines in reverse order: Pasting in reverse with right-click #3816

Smiley lines encoded as binary.
Ungolfed (this is not a one-to-one expanded version of the golfed; this is more 'how it began'):

Clear-Host
For (;;) {
    $wink = -not $wink
    $bits = @(
        'ff0 300c 4002 4002 8421 8421 8421 8421 8421 8421 8001 8001 4422 43c2 300c ff0'
        'ff0 300c 4002 4002 8401 8401 8401 84f9 8401 8401 8001 8001 4422 43c2 300c ff0'
    )[$wink]
    $bits | ForEach-Object -Member Split | ForEach-Object {
        [Convert]::ToString("0x$_",2) |
            ForEach-Object -Member Padleft 16 '0' |
            ForEach-Object -Member ToCharArray |
            ForEach-Object {' ' | Write-Host -BackgroundColor (15*"$_") -NoNewline}
        Write-Output ''
    }
    Start-Sleep -MilliSeconds 500
    [Console]::SetCursorPosition(0,0)
}

Java on Windows, 454 438 384 326 322 bytes

v->{String s="#              #\n",t=" #            #\n",u="  ##        ##\n",w="#    #    %2$c    #\n",x="    ########\n";for(int i=0;;Thread.sleep(500))System.out.printf("\033[H\033[2J%s#    #    %c    #\n"+w+w+"#    #  %s  #\n"+w+w+s+s+" #   #    #   #\n #    ####    #\n"+u+x,x+u+t+t,35-++i%2*3,i%2<1?"  #  ":"#####");}

-53 bytes thanks to @jdt, since although printing \033[H\033[2J to clear the Console doesn't work on my local Windows 10 laptop, it does work in this online compiler:
Try it online (click on the maximize icon of the console, and then click run at the top).
-58 bytes thanks to @WeirdGlyphs

Gif in action:

enter image description here

Explanation:

Pretty straight-forward.

v->{                       // Method with empty unused parameter and no return-type
  String s="#              #\n",t=" #            #\n",u="  ##        ##\n",w="#    #    %2$c    #\n",x="    ########\n";
                           //  Temp Strings for some repeating parts
  for(int i=0              //  Integer `i`, starting at 0
      ;                    //  Loop indefinitely:
       ;                   //    After every iteration:
        Thread.sleep(500)) //     Sleep 500 ms
    System.out.printf(     //     Print:
      "\033[H\033[2J      "//      Two commands to clear the Console:
                           //       `33[H` moves the cursor to the top-left;
                           //       `33[2J` then clears the console
      "%s#    #    %c    #\n"+w+w+"#    #  %s  #\n"+w+w+s+s+" #   #    #   #\n #    ####    #\n"+u+x,
                           //      And then print the output-string,
                           //      and replace the first `%s` with:
      x+u+t+t,             //      temp Strings `x+u+t+t`
                           //      Then replace all its `%c` and `%2$c` with:
      35-                  //       35 minus:
         ++i               //        Increase `i` by 1 first with `++i`
            %2             //        Modulo-2
              *3           //        Multiplied by 3
                           //       (35/'#' if `i` is now odd;
                           //        32/' ' if `i` is now even)
                           //      And finally replace the second `%s` with:
      i%2<1?               //       If `i` is even:
            "  #  "        //        Replace the `%s` with "  #  "
           :               //       Else (`i` is odd)
            "#####");}     //        Replace the `%s` with "#####" instead

Perhaps there is a way to compress the String, but knowing Java, I doubt it would be shorter anyway, so I haven't bothered trying..

Commodore 64 Assembler, 93 Bytes (6502/KickAssembler)

TL;DR

The code generates the graphics into a C64 sprite. It first copies the right side of the sprite from the gfx table, mirrors it to the left side, and then toggles between open and winking eye graphics every ~500ms by using an XOR bitmask. There's also some code for cleaning up the unused sprite graphics memory, as C64 sprites are 24x21 pixels and we use only 16x16 pixel area of that.

Code

.const sprite       = $40   // Sprite graphics will be generated here on zero page
.const data_ptr     = $80   // Free pointer to $20c9, ie. 1st byte of sprite gfx data

*       = $20c9
gfx:    .by %11110000   // Right side of the sprite. Mirrored in code for left side.
        .by %00001100   // Flipped horizontally so that the incrementing data pointer
        .by %11000010   // will end up pointing to the EOR mask table after gfx data
        .by %00100010   // has been copied to sprite.
        .by %00000001
        .by %00000001
        .by %00100001
        .by %00100001
        .by %00100001
        .by %00100001
        .by %00100001
        .by %00100001
        .by %00000010
        .by %00000010
        .by %00001100
        .by %11110000

eormask:.by %00100001^%00000001     // These 6 bytes are an EOR mask that toggles
        .by %00100001^%00000001     // between open and winking eye graphics.
        .by %00100001^%00000001
        .by %00100001^%11111001
        .by %00100001^%00000001
        .by %00100001^%00000001

                            // Start with SYS 8415
        ldx #62             // 2 First we need to create 63 (0-62) bytes of sprite data
        stx $d000           // 3 Save 62 as Sprite #0 X and Y positions (otherwise the
        stx $d001           // 3 sprite would be hidden in top left, behind the border)

draw:   sty sprite,x        // 2 Y=0 here. Clear rightmost byte of sprite pixel row
        dex                 // 1 Decrement sprite byte pointer
        cpx #47             // 2 Repeat until we've cleared sprite pixel rows 17-21
        bcs draw            // 2 If we still have complete rows to clear, repeat
        lda (data_ptr),y    // 2 Load byte from sprite graphics data
        sta sprite,x        // 2 Save as middle byte on sprite pixel row
        inc data_ptr        // 2 Increment sprite graphics data pointer
        dex                 // 1 Decrement sprite byte pointer

        bit $2              // 2 Reverses order of bits in a byte. Found from an old
    !:  php                 // 1 Google groups thread. Just 10 bytes. There's another
        asl                 // 1 10 byte solution but it uses X register which we need
        bne !-              // 2 ourselves. Super clever: Pushes the bits from left
    !:  rol                 // 1 into the carry flag and saves processor flags to stack,
        plp                 // 1 then pulls the flags and pushes carry to the byte from
        bne !-              // 2 the right, effectively reversing the order.

        sta sprite,x        // 2 Save mirrored byte (left side of smiley) to sprite
        dex                 // 1 Decrement sprite byte pointer
        bpl draw            // 2 Loop back until we've drawn all sprite pixel rows

        iny                 // 1 Increment Y to 1
        sty $d015           // 3 Sprite enable register - enable Sprite #0
        sty $07f8           // 3 Sprite #0 pointer - point to the 2nd 64 byte block in memory

anim:   ldy #5              // 2 Animate eye winking: Change 6 bytes of graphics on sprite
        ldx #12             // 2
    !:  lda sprite+16,x     // 2 Load an eye byte from sprite
        eor (data_ptr),y    // 2 EOR it with mask to toggle between open and winking eye
        sta sprite+16,x     // 2 Save toggled eye byte to sprite
        dex                 // 1 Decrement sprite byte pointer three times to point to...
        dex                 // 1 ...the same byte on previous row
        dex                 // 1
        dey                 // 1 Decrement EOR mask table index
        bpl !-              // 2 Loop until all 6 bytes have been changed

delay:  jsr $eeb3           // 3 KERNAL routine: 1ms delay
        jsr $eeb3           // 3 KERNAL routine: 1ms delay
        dex                 // 1 X=250 when we enter the delay loop
        bne delay           // 2 repeat for 250 times
        beq anim            // 2 Animate, delay, rinse and repeat

Usage

Run the program with SYS 8415.

Explanation

Quite a few tricks were used to make the code smaller:

enter image description here

JS, 398 366 246 bytes

New method

Setting image src to a base64 gif of the smiley/winkey.

b=1,setInterval(_=>document.open().write(`<img src=data:image/gif;base64,R0lGODdhEAAQAIAAAAAAAP///yH5BAgKAAAALAAAAAAQABAAAAIpjAOpecftgIMUGVmbx${['Fdrbk2RJ17g+ZHdWIZmRj3wxKHrqagytJvLXg','DcjbmVPJ4LYcpreGFas98Faq5KmMl+gtn8oWA'][b^=1]}AAOw==>`),500)


Old method

Saving image as hex, and printing each pixel to canvas.

a="0ff0300c4002400284218421842184218421842180018001442243c2300c0ff0",d=document,d.write("<canvas>"),x=d.querySelector("canvas").getContext("2d"),f=0,setInterval((_=>(f?a.replace(a.substr(18,21),"018401840184f98401840"):a).match(/.{1,2}/g).map((n=>(+("0x"+n)).toString(2).padStart(8))).join``.split``.map((f=!f,x.reset(),(b,i)=>+b&&x.fillRect(i%16,i/16|0,1,1)))),500)

a="0ff0300c4002400284218421842184218421842180018001442243c2300c0ff0", // Original image converted into bytes (ex. 11111111 -> ff)
d=document,d.write("<canvas>"),x=d.querySelector("canvas").getContext("2d"), // Setting up canvas
f=0, // Image flipper boolean
setInterval(
  _=>(f?a.replace(a.substr(18,21),"018401840184f98401840"):a) // Replacing winking eye bytes every 0.5 sec
    .match(/.{1,2}/g) // Splitting image string into bytes
    .map(n=>(+("0x"+n)).toString(2).padStart(8)) // ... and turning them into 8 bit chunks (empty spaces will be converted to zeroes)
    .join`` // ... flattening all the bits into a single string
    .split``.map(
      (f=!f,x.reset(),(b,i)=>+b&&x.fillRect(i%16,i/16|0,1,1)) // Flipping boolean, clearing canvas, drawing each pixel (i/16|0 same as Math.floor(i/16))
    ),
  500
)

Edits

  1. 398 -> 390: Math.floor() -> ~~
  2. 390 -> 386: forEach -> map
  3. 386 -> 381: Removed setting canvas to a var, removed semicolon from the end.
  4. Added expanded code and explanation.
  5. 381 -> 377: Removed extra parentheses and one NOT (!)
  6. 377 -> 376: Turned function parentheses into _
  7. 376 -> 374: Changed how I floor the y position (from ~~(i/16) to i/16|0).
  8. 374 -> 372: Removed extra {} from around draw func.
  9. 372 -> 366: Converted two function calls from ("") -> to `` (thanks @Weird Glyphs), and removed padding 0 from padStart, as spaces will be falsy.
  10. 366 -> 246: Changed to a simpler solution using base64 gifs.

HTML + CSS + JS, 588 432 bytes

Disclaimer: Entirely based on @A.L's answer.

New version: encoded the bitmap as a base-5 BitInt -- Oops, now below 512 bytes.

<style>.b{background:#000}</style><table><script>w=0x3e069e01a401efc9ea7fe983ad46adaa384a346fdd0397dccfb1907ddd5e9ac3c2c69c82ffc3ee9c41e812d4f1094c58672f9e16b215bb9ee03929e83e12580d4b5ec76d63ece8aea0d85ef67f4n
s=1
u=document
setInterval(_=>{for(t of'ft')for(e of u.querySelectorAll('.'+t))e.classList.toggle('b',s^t=='t')
s^=1},500)
for(l=271;c=w%5n,l--;w/=5n)u.write(c==4?'<tr>':`<td class=${'0bft'[c]} width=7 height=9>`)</script>

Produced by the following Python script from the bitmap in @A.L's answer:

for ch in reversed(wink):
    result *= 5
    if ch == '\n':
        result += 4
    else:
        result += int(ch)

Edits:

Old:

Still 77 bytes to go before we can make it 512.

<style>*{margin:0;padding:0;border:0}.b{background:#000}</style><table><script>w="0000bbbbbbbb0000D00bb00000000bb00D0b000000000000b0D0b000000000000b0Db0000b0000t0000bDb0000b0000t0000bDb0000b0000t0000bDb0000b00ffbff00bDb0000b0000t0000bDb0000b0000t0000bDb00000000000000bDb00000000000000bD0b000b0000b000b0D0b0000bbbb0000b0D00bb00000000bb00D0000bbbbbbbb0000"
s=1
u=document
f=()=>{for(t of'ft')for(e of u.querySelectorAll('.'+t))e.classList.toggle('b',s^t=='t')
s^=1
setTimeout(f,500)}
f()
w.split('D').map(l=>{u.write('<tr>')
for(c of l)u.write(`<td class=${c} width=9 height=9>`)})</script>

ZX Spectrum Basic, 293 bytes

   1 OVER SGN PI: LET a$="2 2 13
 3-13 5 03 3 12 1 23-1 33 0 53 1
 32 2143 3 13 5 03 3-1214 23 1 3
3 0 53-1 32 5 32 6 23 3 0210 32 
5 63 0 52 8 83 4 0425552 8 83 4 
0210 63 0 542555210 63 0 555555"
: FOR i=VAL "5" TO LEN a$ STEP V
AL "5": LET x=VAL a$(i-INT PI TO
 i-VAL "2"): LET y=VAL a$(i-SGN 
PI TO i): GO TO VAL a$(i-VAL "4"
)
   2 PLOT x,y: NEXT i
   3 DRAW x,y: NEXT i
   4 PAUSE x: NEXT i
   5 LET i=x+y: NEXT i

Line 1 is very long but it looks like that on the ZX Spectrum because its lines are only 32 characters wide. It starts by turning on overplot, which makes it easy to erase. It then defines a string of compressed plot instructions as sets of five characters, the first being the line number to execute, then two pairs of two-digit numbers being the x and y values. The string is looped over, the values are extracted and the appropriate line is executed. The 55555 at the end actually causes the code to loop back to a previous point of the string. The timing is a little shaky because ZX Spectrum Basic is quite slow but the 425s can be lowered a little to speed it up. Unfortunately I don't have any video capture facility to show you what the live output looks like, so here are two screen captures from an emulator:

wink off

wink on

HTML + CSS + JS, 1024 bytes

I didn't try to optimize it much, I just tried to start from an ASCII representation of the pixels (maybe it can help other people), then minimized it to 1 kb.

The code will draw a HTML table with the different possible states:

A JS loop will switch on and off the pixels of the right eye.

Quickly minimized version and output

<!DOCTYPE html>
<html lang="en">
<head>
    <title></title>
    <style>
        * {margin: 0;padding: 0;border: none;}td {width: 10px;height: 10px;}.t1 {background: #000}
    </style>
    <script>
        var wink = `0000111111110000
0011000000001100
0100000000000010
0100000000000010
1000010000300001
1000010000300001
1000010000300001
1000010022122001
1000010000300001
1000010000300001
1000000000000001
1000000000000001
0100010000100010
0100001111000010
0011000000001100
0000111111110000`;
        var state = true;
        function flipState() {
            document.querySelectorAll(state ? '.t2' : '.t3').forEach((element) => {element.classList.add('t1');});document.querySelectorAll(state ? '.t3': '.t2').forEach((element) => {element.classList.remove('t1');});state = !state;setTimeout('flipState()', 500);}
        flipState();
    </script>
</head><body><table><script>
        wink.split('\n').forEach(function (line) {document.write('<tr>');line.split('').forEach(function (char) {document.write('<td class="t' + char+ '"></td>');});document.write('</tr>');});
    </script></table></body>
</html>

Ungolfed

Use any tool to reformat this code with new lines between blocks.