Loading Circle Animation


Write a full program that takes no input (or has an unused input) that displays exactly the following ASCII animation for as long as possible or only 8 frames.

Frame 1:

#  #

Frame 2:


Frame 3:

#  #

Frame 4:

#  #
#  #

(Note that the line at the end is empty.)

Frame 5:

#  #

Frame 6:


Frame 7:

#  #

Frame 8:

#  #
#  #

(Note that the line at the start is empty.)


  • The program takes no input (or has an unused input).
  • The animation can start at any frame, but must animate in sequential order.
  • The animation can be in reverse order.
  • The program has to run as long as possible or for exactly 8 frames.
  • The animation has to be displayed at the same position on screen for every frame.
  • Trailing/leading whitespace is okay, as long as the animation looks right.
  • # and space can be replaced with different (not a newline) distinct characters that are each 1 byte.
  • There should be an additional pause of any length but more than 0 ms after an entire frame is drawn.
  • This is , so the shortest answer in bytes wins.
  • 1
    \$\begingroup\$ 1. The animation has to be displayed at the same position on screen for every frame. Can this be done by writing to stdout and letting the screen scroll so that the animation appears at the bottom? Otherwise you're excluding a lot of languages. \$\endgroup\$ Dec 23, 2021 at 23:03
  • 2
    \$\begingroup\$ 2 Flickering is fine as long as the user can tell what is being animated. This may require delay, by one of: pausing by sleep or in a delay loop; printing each symbol multiple times (within the delay loop); waiting for a keypress from the user. Which are acceptable? Note that this requirement is subjective, as it could depend on both the user's eyes and the hardware used. You could specify a frame rate, or allow it to run as fast as the hardware allows, even if the user can't see what's going on, so long as the output is correct. \$\endgroup\$ Dec 23, 2021 at 23:10
  • 1
    \$\begingroup\$ @Yousername You should add the pause requirement to the post. \$\endgroup\$
    – Adám
    Dec 23, 2021 at 23:57
  • 1
    \$\begingroup\$ Note that the pause requirement is probably unobservable, unless you set a minimum of say 100 ms. How do you tell the normal delay caused from the program running each iteration from an additional delay? \$\endgroup\$
    – Luis Mendo
    Dec 24, 2021 at 11:20
  • 1
    \$\begingroup\$ @KevinCruijssen Yes, that is included in the rules. \$\endgroup\$
    – Yousername
    Dec 24, 2021 at 18:15

15 Answers 15


convey, 108 bytes

You never said that the program must have an output, you said in fact "a program that displays exactly the following ASCII animation"

And my program has that animation inside it:

 >>>v  >>>v
>,<<<  ,<<<
^    >v'######'[
^<< >^,'######'[
>>^ ^<<

Try it online!

Here is the program runing:

enter image description here

And in the center you can see... THE LOAD ANIMATION :v

enter image description here

Really the programs doesn't start by an state of the animation, it waits 2 cycles before start but... its cool... i supose?


CP-1610 machine code, 33 DECLEs1 ≈ 42 bytes2

1. A CP-1610 opcode is encoded with a 10-bit value (0x000 to 0x3FF), known as a 'DECLE'.
2. As per the exception described in this meta answer, the exact score is 41.25 bytes (330 bits)

Uses $'s and spaces. Loops forever. Each frame is displayed during ~650 ms. This is a full program mapped in the memory range $4800-$4820. (The Intellivision bootstrap code will jump at $4800 after a reset if it finds something there.)

                      BT    EQU   $200      ; BACKTAB address
                            ROMW  10        ; 10-bit ROM
                            ORG   $4800     ; map the program at $4800

4800   0002                 EIS             ; enable interrupts

4801   02B8 007E            MVII  #$7E, R0  ; load %01111110 in R0
4803   0044                 SWAP  R0,   2   ; expand it to %0111111001111110

4804   00BD           loop: MOVR  R7,   R5  ; \_ R5 = pointer to
4805   02FD 0013            ADDI  #p-$, R5  ; /       BACKTAB positions

4807   0081           draw: MOVR  R0,   R1  ; copy R0 to R1
4808   03B9 0020            ANDI  #32,  R1  ; isolate the 6th bit
480A   02F9 0007            ADDI  #7,   R1  ; add 7 to draw in white
480C   02AA                 MVI@  R5,   R2  ; load the position in R2
480D   0251                 MVO@  R1,   R2  ; write the character
480E   0058                 SLLC  R0        ; \_ rotate the pattern
480F   0028                 ADCR  R0        ; /  to the left
4810   0092                 TSTR  R2        ; is R2 equal to 0?
4811   022C 000B            BNEQ  draw      ; if not, draw again

4813   0012           idle: DECR  R2        ; idle loop: decrement R2
4814   022B 0002            BMI   idle      ; until it looks positive

4816   0220 0013            B     loop      ; restart

                            ; positions
4818   0229 023D ...  p:    DECLE BT+41, BT+61, BT+82, BT+83
481C   0240 022C ...        DECLE BT+64, BT+44, BT+23, BT+22
4820   0000                 DECLE 0



made with jzIntv


HTML5 + CSS3, 190 + 98 = 288 162 + 102 = 264 86 + 170 166 165 = 256 252 251 bytes

tt{--c:1}div{--c:2}div tt{--c:3}a+a{--d:calc(7 - var(--c,0))}a{animation:a steps(4,start)1s calc(var(--d,var(--c,0))/8*-1s)infinite}@keyframes a{to{visibility:hidden
<pre> <a>#</a><a>#</a>
<tt><a>#</a>  <a>#</a><div><a>#</a>  <a>#</a>
<tt> <a>#</a><a>#

Explanation: This is based on my Stack Overflow answer to Imitating a blink tag with CSS3 animations, however the number of steps is set to 4 to reduce the duty cycle from 80% to 75% and CSS variables are used to offset the animation of each # character. Edit: Saved at least 24 bytes thanks to @IsmaelMiguel. Saved 1 byte by stealing @ccprog's 1s/8 trick.

  • 1
    \$\begingroup\$ To save 1 byte, you can remove the ; in the @keyframes rule. To save a few more, you can use opacity:0. Technically, only 2 # are hidden at any point. And it looks a lot more appealing as well. You can also remove the space before start and replace 125ms for .1s to save 3 bytes. \$\endgroup\$ Dec 24, 2021 at 17:31
  • \$\begingroup\$ You can save 8 bytes (or so) by moving --c:0 from the first <a> to a{} (in the CSS). The first # will look like this: <a>#</a> and the CSS will look like this: a{--c:0;...}. If you remove all the quotes, you can save 14 bytes as well. \$\endgroup\$ Dec 24, 2021 at 17:38
  • \$\begingroup\$ @IsmaelMiguel Changing to opacity or changing the timing doesn't look so good but thanks for pointing out the other savings. \$\endgroup\$
    – Neil
    Dec 24, 2021 at 22:11
  • \$\begingroup\$ You're welcome. I personally like those changes. I found a way to save EVEN MORE bytes. You can use invalid tags to assign the values to --c, without changing the result. Also, you can remove the last } to save 1 byte. The CSS becomes pre>*{--c:0;animation:a steps(4,start)1s calc(var(--c)*-125ms)infinite}@keyframes a{to{visibility:hidden}}c{--c:7}d{--c:1}e{--c:6}f{--c:2}h{--c:5}j{--c:3}k{--c:4 and the HTML becomes <pre> <a>#</a><c>#</c>&#10;<d>#</d> <e>#</e>&#10;<f>#</f> <h>#</h>&#10; <j>#</j><k>#</k> (replace &#10; for an actual newline). The new size is 78+161=239 bytes. \$\endgroup\$ Dec 25, 2021 at 2:03
  • \$\begingroup\$ I found yet another saving! You can remove the --c:0; and change the var(--c) to var(--c,0). This should save an additional 4 bytes, to a size of 78+157=235 bytes. The final CSS is pre>*{animation:a steps(4,start)1s calc(var(--c,0)*-125ms)infinite}@keyframes a{to{visibility:hidden}}c{--c:7}d{--c:1}e{--c:6}f{--c:2}h{--c:5}j{--c:3}k{--c:4. \$\endgroup\$ Dec 25, 2021 at 2:13

Charcoal, 21 19 bytes


Try it online! Link is to verbose version of code. Outputs 8 frames. Explanation:


Repeats 8 times with a 10ms delay in between each repeat, dumping the canvas to the terminal each time. (Note that this just shows up as control characters in TIO.)


Draw an octagon using the multidirectional *, which means all directions in turn, in the order right, down right, down, down left, left, up left, up, up right. Each side of the octagon is 2 characters long, and the octagon is drawn using a cyclic permutation of the string # ##### (obtained through reversing the cyclic extension of ##### # which then gets implicitly truncated to 8 characters) depending on the loop index.

For offline use you may prefer 1 second frames for the same byte count:


The animation can be extended to 1000 frames (125 whole cycles) at a cost of two bytes:


Try it online! Link is to verbose version of code. Or with 1s frames:


An infinite animation would take 27 25 bytes (10ms and 1s frames):


MATL, 39 34 bytes


Try it at MATL online!

How it works

`            % Do..while
  3&Xx       %   Pause for 300 ms and clear screen
  0          %   Push 0
  '=<.Ic^w'  %   Push this string (*)
  F          %   Push false (**)
  O          %   Push 0
  8:         %   Push [1 2 ... 8]
  @          %   Push current iteration index, k
  YS         %   Rotate right by k units. For example, in the 1st iteration
             %   (k=1) this gives [8 1 2 3 4 5 6 7]
  h          %   Concatenate: prepends 0. This gives [0 8 1 2 3 4 5 6 7]
             %   in the 1st iteration
  Za         %   Base conversion. Converts the string (*) from the base
             %   formed by all printable ASCII chars except single quote
             %   (represented by the false input, (**)) to the base (***).
             %   This gives [6 5 0 7 0 0 4 8 0 0 3 0 1 2] in the 1st iteration
  h          %   Concatenate: prepends 0. Gives [0 6 5 0 7 0 0 4 8 0 0 3 0 1 2]
  4e         %   Reshape into 4-row matrix in column-major order. This pads with
             %   a 0 at the end to have a number of entries multiple of 4. Gives
             %   [ 0 7 8 0 ;
             %     6 0 0 1 ;
             %     5 0 0 2 ;
             %     0 4 3 0 ]
             %   in the 1st iteration. For other iterations the nonzero values
             %   will be rotated with respect to the above
  H>         %   Greater than 0? Gives
             %   [ 0 1 1 0 ;
             %     1 0 0 0 ;
             %     1 0 0 0 ;
             %     0 1 1 0 ]
  Zc         %   Convert nonzeros to '#' and zeros to char 0 (shown as space)
             %   [ ' ## ' ;
             %     '#   ' ;
             %     '#   ' ;
             %     ' ## ' ]
  D          %   Display
  T          %   Push true
             % End (implicit). A new iteration is run because the top of the
             % stack is true

APL (Dyalog Unicode), 60 55 bytes

Full program. Requires 0-based indexing. Uses 0 and 1 instead of and #. Waits 1 seconds between frames and stops after the 8th.

⎕ED&'a'⋄{⎕DL≡a∘←⊃⍤⍕¨4 4⍴'012080037004065'∊⍕1+8|⍵+⍳6}¨⍳8

'a' the (eventual) variable name "a"
& in a background thread:
⎕ED open an EDit window on that name


⍳8 generate the integer sequence 0…7
{ on each element of that, apply the following anonymous lambda:
⍳6 generate the integer sequence 0…5
⍵+ add the argument to that
8| find the division remainder of that when divided by 8
1+ increment that
 format as space-separated text
'012080037004065'∊ for each character in this string, check if it is a member (1) or not (0) of that string
4 4⍴ reshape those 16 1s and 0s into a 4-by-4 matrix
⍕¨ format each of those numbers as a 1-character list
⊃¨ get the first character of each
a∘← globally assign that value to the variable a (the edit window will update)
 get the nesting depth of that matrix (it is 1)
⎕DL DeLay that many seconds

Old version in action: screencast


05AB1E, 36 bytes


If only 05AB1E had a builtin to clear the console.. :/

Starts at frame 6, and rotates indefinitely in reversed order. Uses 0 instead of # as character, although could alternatively use 1/2 for the same byte-count by replacing the ¾ with X/Y respectively. Sleeps for 100 ms between each print, but could alternatively sleep any of these times for the same byte-count: 1/2/3/4/5/6/7/8/9/T (10)/ (26)/ (36)/ (95)/ (255)/ (256)/ (1000), by replacing the т.


[                   # Loop indefinitely: 
 2                  #  Push 2
 ¾                  #  Push 0
  6×                #  Repeat it 6 times as string: "000000"
    2ú              #  Pad with 2 leading spaces: "  000000"
      N._           #  Rotate the string the loop-index amount of times towards the left
 7Ý                 #  Push list [0,1,2,3,4,5,6,7]
         .Λ         #  Use the modifiable Canvas builtin with these three options
           ,        #  Pop and print it with trailing newline
 т.W                #  Sleep for 100 ms
 ’IEx.Helpers.Š‡’   #  Push dictionary string "IEx.Helpers.clear"
                 .E #  Evaluate and execute it as Elixir code

See this 05AB1E tip of mine (section How to use the dictionary?) to understand why ’IEx.Helpers.Š‡’ is "IEx.Helpers.clear".

The Canvas Builtin uses three arguments to draw a shape:

  • Length of the lines we want to draw
  • Character/string to draw
  • The direction to draw in, where each digit represents a certain direction:
7   0   1
  ↖ ↑ ↗
6 ← X → 2
  ↙ ↓ ↘
5   4   3

2¾6×2úN._7Ý creates the following Canvas arguments:

  • Length: 2
  • Characters: " 000000" (potentially rotated)
  • Directions: [0,1,2,3,4,5,6,7], which translate to \$[↑,↗,→,↘,↓,↙,←,↖]\$

Try the first two steps online.

Step 1: Draw 2 characters (" ") in direction 0/:


Step 2: Draw 2-1 characters ("0") in direction 1/:


Step 3: Draw 2-1 character ("0") in direction 2/:


Step 4: Draw 2-1 character ("0") in direction 3/:



Step 7: Draw 2-1 character ("0") in direction 6/:


Step 8: Draw 2-1 character (" ") in direction 7/:


See this 05AB1E tip of mine for an in-depth explanation of the Canvas builtin.

I don't have a gif to see the animation in action, because IEx.Helpers.clear (or any clear through Elixir-eval for that matter) doesn't seem to work on Windows machines.. I tried to enable ANSI escape codes in the Windows Console by modifying the Registry on my PC, but wasn't able to get it to work. This should work as intended on a non-Windows machines, though.

As alternative, here the outputs where the clear-command is ignored and the infinite loop [ is replaced with a loop of eight 8F: Try it online.


Stax, 32 bytes


Run and debug it

Uses JS requestAnimationFrame to delay a frame between each step. Here is one which uses a 30-frame delay for better inspection: link

Idea is similar to MATL. Lack of vectorization makes it somewhat bulky.


Python 2, 158 bytes

import time
while 1:
 for w in range(8):
	for x in'48966886698469906912611621960996'[w*4:w*4+4]:print('000'+bin(int(x))[2:])[-4:]

This version does not work on Try it online!

Uses 0 for and 1 for #.

Here is a version (non competing) without the clear screens and pauses (one of which blows up TIO) which displays each stage of the animation for each cycle and allows users without a Python 2.7 environment to see the output. For me, this covers the most important and interesting part of the challenge. Clearing the screen and adding delays is really just commentary IMHO.

Python 2, 118 bytes

for w in range(8):
 for x in'48966886698469906912611621960996'[w*4:w*4+4]:print('000'+bin(int(x))[2:])[-4:]
 print ' '

Try it online!

  • 2
    \$\begingroup\$ Do you really need the print'\n'*99? Just resize your terminal to 4x4 ;) \$\endgroup\$
    – emanresu A
    Dec 26, 2021 at 0:21
  • \$\begingroup\$ Nice idea but the first frame will include the command line which means the output is not in the same position as the remaining frames \$\endgroup\$
    – ElPedro
    Dec 26, 2021 at 18:46

Wolfram Mathematica 89 bytes


This gif is a bit glitchy, but the above animation does appear correctly, and play automatically and continuously, within a Mathematica notebook.

enter image description here

How it works:

The ASCII characters in the challenge are arranged as vertices on an octagon rather than points on a circle (the left and right pairs are vertically aligned, and the top and bottom pairs are horizontally aligned), so I used CirclePoints@8 to get the coordinates of the 8 vertices of a regular octagon centered about {0,0}. I rotated this coordinate list successively to the left by u, and then dropped the first two points. I used ListPlot to plot these, and then animated this, increasing u from 0 to 7 in increments of 1.

The characters are displayed as plot points.

Even though ,PlotRange->{{-1,1},{-1,1}} costs 27 bytes, an explicit plot range specification was needed to maintain the aspect ratio of the plot as the points were rotated, so that the points would always appear at the same position on the screen.

Alternately, for three less bytes (86), one could use ListPolarPlot to put the six points on an actual circle (separated by angles of Pi/4), rather than the vertices of an octagon:

ListPolarPlot[1 &~Array~6,DataRange->{u,5*Pi/4+u},PlotRange->Full]~Animate~{u,0,7,Pi/4} 

This latter code would have been longer than the former, except that here the position and aspect ratio can be fixed using PlotRange->Full instead of PlotRange->{{-1,1},{-1,1}}.


pug + CSS, 81 + 135 = 216 87 + 123 = 210 bytes

while o>>=1
 if o&1
  a(style=`--i:-${((i&1?7:0)^i++>>1)/8}s`) 0
body{display:grid;grid:none/repeat(4,1ch)}a{animation:a steps(4,start)1s var(--i)infinite}@keyframes a{to{visibility:hidden

Watch it on CodePen. Click on the downward arrow in the HTML area to see the compiled HTML.

The animation is inspired by Neil's answer, but the custom CSS variables are injected by pug.

The characters are ordered with a CSS grid, four items per row with a width of 1ch each.

1ch is defined as the exact advance of the 0 character in that font. That is the reason for displaying it, so the output is exactly aligned as if it was formatted with <pre>.

Edit: saved 6 bytes with the help of Ismael Miguel.

  • \$\begingroup\$ Nice use of 1s/8 instead of 125ms! \$\endgroup\$
    – Neil
    Dec 26, 2021 at 21:24
  • \$\begingroup\$ (Although should that be -1s/8?) \$\endgroup\$
    – Neil
    Dec 26, 2021 at 21:25
  • \$\begingroup\$ One less byte, and reverse direction is allowed. \$\endgroup\$
    – ccprog
    Dec 26, 2021 at 22:36
  • \$\begingroup\$ Actually it's to make the animation start properly - your first frame has everything visible. \$\endgroup\$
    – Neil
    Dec 27, 2021 at 1:17
  • \$\begingroup\$ Oh! You are right, I didn't notice that. \$\endgroup\$
    – ccprog
    Dec 27, 2021 at 11:54

C (gcc), 162 149 bytes

  • -13 thanks to ceilingcat

Loops forever. Each line is encoded in 4-bit segments and shifted to produce the line for the current frame, with increasingly long sleep() values. The screen is cleared with a form feed character, which should work in most terminal emulators.

It would be possible to save 3 bytes by not initializing the starting frame, but there is a chance that a negative number could be entered before the function is called, resulting in a frame with a 0-second delay.

i,h;f(c){for(c=0;puts("\f");sleep(++c))for(i=4;i--;puts(""))for(h=L"\x66402666\x98891199\x88999119\x46666620"[i]>>c%8*4&15;h;h/=2)printf(L" #"+h%2);}

Try it online!


HTML5 + CSS3 - 78 + 156 = 234 bytes

This answer is entirely based on @Neil's answer.

This was offered as a size reduction of his original answer, which he allowed me to share as an answer.
This answer includes a 1 byte trick as well, taken from the original answer.

pre>*{animation:a steps(4,start)1s calc(var(--c,0)*-125ms)infinite}c{--c:7}d{--c:1}e{--c:6}f{--c:2}h{--c:5}j{--c:3}k{--c:4}@keyframes a{to{visibility:hidden
<pre> <a>#</a><c>#</c>
<d>#</d>  <e>#</e>
<f>#</f>  <h>#</h>

This relies entirely on invalid tags, to calculate the CSS animation delays. This allows to shorten the selectors a lot, and is shorter than pre-calculating the delays.

If we're allowed to take certain liberties with the presentation of the loading animation, it's possible to reduce the size a few more bytes.

Using opacity instead of visibility, it's possible to have 2 hidden and all others partially visible.
If the timing can be flexible, it is possible to save some bytes as well.

This also includes a suggestion by @Neil, to use 1s steps for 8s, shaving this one down by 2 bytes.

As such, here's a 78 + 145 = 223 bytes long solution, which might not be valid (but looks prettier, in my opinion):

pre>*{animation:a steps(4,start)8s calc(var(--c,0)*-1s)infinite}c{--c:7}d{--c:1}e{--c:6}f{--c:2}h{--c:5}j{--c:3}k{--c:4}@keyframes a{to{opacity:0
<pre> <a>#</a><c>#</c>
<d>#</d>  <e>#</e>
<f>#</f>  <h>#</h>

Please remember to upvote @Neil's answer before considering upvoting mine.

  • 1
    \$\begingroup\$ Both done and respect for your last comment in the answer. \$\endgroup\$
    – ElPedro
    Dec 27, 2021 at 14:40
  • 1
    \$\begingroup\$ @ElPedro Thank you. It's only the fair thing to do, as he's the one that came up with the initial idea that led me to this version. \$\endgroup\$ Dec 27, 2021 at 16:45
  • 1
    \$\begingroup\$ Agree completely. That's why this community works for me. \$\endgroup\$
    – ElPedro
    Dec 27, 2021 at 19:12

Python 3.8 (pre-release), 91 bytes

Not my favorite because there's no delay...

Also this only works with a terminal that supports moving the cursor with \x1b[r;cH, and clearing the terminal with \x1b[2J (... It works for me in termux on Android so far...)

[print('\x1b[2J','\x1b[%c;%cH*'*6%(*('2131424334241312'*2)[b*2:b*2+12],))for b in range(8)]

Try it online!

Python 3.8 (pre-release), 119 bytes

Here's a more realistic answer with the most obvious delay.

import time;[print('\x1b[2J','\x1b[%c;%cH*'*6%(*('2131424334241312'*2)[b*2:b*2+12],))or time.sleep(1)for b in range(8)]

Try it online!


JavaScript, 93 bytes

setInterval(_=>console.log(` 01
7  2
6  3
 54`.replace(/\S/g,n=>(i-n)%8<6?'#':' ',i++)),i=99)


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.