Animation

From Rosetta Code
Task
Animation
You are encouraged to solve this task according to the task description, using any language you may know.


Animation is integral to many parts of GUIs, including both the fancy effects when things change used in window managers, and of course games.   The core of any animation system is a scheme for periodically changing the display while still remaining responsive to the user.   This task demonstrates this.


Task

Create a window containing the string "Hello World! " (the trailing space is significant).

Make the text appear to be rotating right by periodically removing one letter from the end of the string and attaching it to the front.

When the user clicks on the (windowed) text, it should reverse its direction.

AArch64 Assembly

Works with: as version Raspberry Pi 3B version Buster 64 bits
or android 64 bits with application Termux
/* Programme assembleur ARM Raspberry ou Android */
/* programme animletter64.s */
/* REMARK: This program was written for android with the termux app.
 It works very well on raspberry pi but in this case the memory access relocation instructions 
 can be simplified.
 
 For simplicity, this program does not use X11 structure definitions.
*/

/*******************************************/
/* Constantes file                         */
/*******************************************/
/* for this file see task include a file in language AArch64 assembly*/
.include "../includeConstantesARM64.inc"

/* constantes X11 */
.equ KeyPressed,    2
.equ ButtonPress,   4
.equ MotionNotify,  6
.equ EnterNotify,   7
.equ LeaveNotify,   8
.equ Expose,        12
.equ ClientMessage, 33
.equ KeyPressMask,  1
.equ ButtonPressMask,     4 
.equ ButtonReleaseMask,   8 
.equ ExposureMask,        1<<15
.equ StructureNotifyMask, 1<<17
.equ EnterWindowMask,     1<<4
.equ LeaveWindowMask,     1<<5 
.equ ConfigureNotify,     22

.equ GCForeground,   1<<2
.equ GCBackground,   1<<3
.equ GCLine_width,   1<<4
.equ GCLine_style,   1<<5
.equ GCFont,         1<<14

.equ CWBackPixel,    1<<1
.equ CWBorderPixel,  1<<3
.equ CWEventMask,    1<<11
.equ CWX,            1<<0
.equ CWY,            1<<1
.equ CWWidth,        1<<2
.equ CWHeight,       1<<3
.equ CWBorderWidth,  1<<4
.equ CWSibling,      1<<5 
.equ CWStackMode,    1<<6


/* constantes program */
.equ WINDOWWIDTH,       400
.equ WINDOWHEIGHT,      300

/*********************************/
/* initialized datas             */
/*********************************/
.data
szWindowName:            .asciz "Windows Raspberry"
szRetourLigne:           .asciz  "\n"
szMessDebutPgm:          .asciz "Program start. \n"
szMessFinPgm:            .asciz "Program end OK.\n"
szMessErreur:            .asciz "Server X not found.\n"
szMessErrfen:            .asciz "Can not create window.\n"
szMessErreurX11:         .asciz "Error call function X11. \n"
szMessErrGc:             .asciz "Can not create graphics context.\n"
szTitreFenRed:           .asciz "Pi"    
szTexte1:                .asciz "Hello world!  "
.equ LGTEXTE1, . - szTexte1

szLibDW:           .asciz "WM_DELETE_WINDOW"                        // special label for correct close error

.align 8
stXGCValues:       .quad 0,0,0xFFA0A0A0,0,0,0,0,0,0,0,0,0,0,0,0,0,0   // for foreground color gris1
//stXGCValues:       .quad 0,0,0x00000000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0   // for foreground color black
stXGCValues1:      .quad 0,0,0x00FFFFFF,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0   // for foreground color white
stXGCValues2:      .quad 0,0,0x0000FF00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0   // for foreground color green 


/*********************************/
/* no initialized datas          */
/*********************************/
.bss  
.align 8
sZoneConv:        .skip 24
qDisplay:         .skip 8               // Display address
qEcran:           .skip 8               // Screen address
ptGC1:            .skip 8               // Graphic Context pointer
qSens:            .skip 8               // direction
qKey:             .skip 8               // touch value
wmDeleteMessage:  .skip 8               // ident close message
stEvent:          .skip 400             // event structure
sBuffer:          .skip 80
sbuffer1:         .skip 80
/*********************************/
/*  code section                 */
/*********************************/
.text
.global main 
main:                           // programm entry
    adr x0,qOfszMessDebutPgm
    ldr x1,[x0]
    add x0,x0,x1
    bl affichageMess
/*  Attention  x28  display address
               x27  window ident
               x26  ident GC1 
               x24  ident GC2
               */
    mov x0,#0                  // open X server
    bl XOpenDisplay
    cbz x0,99f                 // no X server ?
   
    adr x1,qOfqDisplay         // ok
    ldr x2,[x1]
    add x2,x2,x1
    str x0,[x2]                // store  DISPLAY address
    mov x28,x0                 // and register 28
    ldr x2,[x0,264]            // load screen address in display structure 
    adr x3,qOfqEcran
    ldr x1,[x3]
    add x1,x1,x3
    str x2,[x1]                // store   default_screen

    mov x2,x0
    ldr x0,[x2,#232]           // screens list address
    ldr x5,[x0,#+88]           // white pixel
    ldr x3,[x0,#+96]           // black pixel
    ldr x4,[x0,#+56]           // bits par pixel
    ldr x1,[x0,#+16]           // root windows
    /*****************************/
    /* WINDOW CREATION           */
    /*****************************/
    mov x0,x28                 // display address
    mov x2,#10                 // position X 
    mov x3,#20                 // position Y
    mov x4,WINDOWWIDTH         // width
    mov x5,WINDOWHEIGHT        // height
    mov x6,1                   // bordure width
    mov x7,0                   // pixel bordure = black
    ldr x8,qGris1               // background color
    str x8,[sp,-16]!           // by stack parameter
    bl XCreateSimpleWindow
    add sp,sp,16               // stack alignement (one parameter)
    cbz x0,98f                 // create error ?
    mov x27,x0                 // save ident window
    
    /*****************************/
    /* add window property       */
    /*****************************/
    mov x0,x28                   // display address
    mov x1,x27                   // window address
    adr x2,qOfszWindowName       // window name
    ldr x5,[x2]
    add x2,x2,x5
    adr x3,qOfszTitreFenRed      // window name reduced
    ldr x5,[x3]
    add x3,x3,x5
    mov x4,#0                    // parameters not use
    mov x5,#0
    mov x6,#0
    mov x7,#0
    bl XSetStandardProperties
    /**************************************/
    /* for correction window close error  */
    /**************************************/
    mov x0,x28                   // display address
    adr x1,qOfszLibDW           // atom address
    ldr x5,[x1]
    add x1,x1,x5
    mov x2,#1                   // False  créate atom if not exists
    bl XInternAtom
    cmp x0,#0                   // error X11 ?
    ble 97f                     // Modif avril 22 pour android (ble pour raspberry)
    adr x1,qOfwmDeleteMessage  // recept address
    ldr x5,[x1]
    add x1,x1,x5
    str x0,[x1]
    mov x2,x1                   // return address
    mov x0,x28                   // display address
    mov x1,x27                   // window address
    mov x3,#1                   // number of protocols
    bl XSetWMProtocols
    cmp x0,#0                   // error X11 ?
    ble 97f
    
    mov x0,x28                 // Display address
    mov x1,x27                 // window ident
    bl creationGC
    /****************************/
    /* window display on screen */
    /****************************/
    mov x0,x28                   // display address
    mov x1,x27                   // window ident
    bl XMapWindow

                                // authorization of seizures
    mov x0,x28                  // display address
    mov x1,x27                  // window ident
    ldr x2,qSaisieMask          // autorizations mask
    bl XSelectInput
    
    adr x0,qOfszTexte1           // string address
    ldr x4,[x0]
    add x0,x0,x4
    adr x1,qOfsbuffer1           // buffer address
    ldr x4,[x1]
    add x1,x1,x4
    mov x2,#0
1:                               // loop copy character
    ldrb w3,[x0,x2]
    strb w3,[x1,x2]
    cmp x3,#0
    beq 2f
    add x2,x2,#1
    b 1b
2:
    /****************************/
    /* Events loop              */
    /****************************/
3:
    mov x0,x28                  // display address
    bl XPending                 // loading the number of events
    cmp x0,#0
    bne 4f                      // a event is occurring
    adr x3,qOfsbuffer1
    ldr x5,[x3]
    add x3,x3,x5
    mov x0,x28                  // display address
    mov x1,x27                  // window ident
    mov x2,x26                  // graphic context 
    bl animate                  // string display and move
    b 3b                        // and loop

4:
    mov x0,x28                  // adresse du display
    adr x1,qOfstEvent           // adresse structure evenements
    ldr x2,[x1]
    add x1,x1,x2
    mov x25,x1                  // save adresse structure evenement
    bl XNextEvent
    
    ldr w0,[x25]                // code évenement
    cmp x0,#KeyPressed          // appui touche ?
    bne 5f
                                // cas d'une touche
    mov x0,x25
    adr x1,qOfsBuffer           // adresse buffer
    ldr x2,[x1]
    add x1,x1,x2
    mov x2,#255
    adr x3,qOfqKey              // adresse zone clé
    ldr x4,[x3]
    add x3,x3,x4
    mov x4,#0
    bl XLookupString            // recup touche dans buffer
    cmp x0,#1                   // touche caractères ?
    bne 6f

    adr x0,qOfsBuffer           // adresse du buffer
    ldr x2,[x0]
    add x0,x0,x2
    ldrb w0,[x0]                // charge le 1er caractère du buffer
    cmp w0,#0x71                // caractere q ?
    beq 7f                      // oui -> fin
    b 6f
5:
    /************************************/
    /*  clic mouse button   */
    /************************************/
    cmp x0,ButtonPress         // clic mouse buton
    bne 51f
    adr x0,qOfstEvent
    ldr x5,[x0]
    add x0,x0,x5
    ldr w1,[x0,#+64]            // position X mouse clic
    ldr w2,[x0,#+68]            // position Y
    mov x0,x1
    mov x0,x2
    cmp x1,#50                  // test if position clic is on the screen string approx.
    blt 6f
    cmp x1,#150
    bgt 6f
    cmp x2,#80
    blt 6f
    cmp x2,#105
    bgt 6f
    adr x1,qOfqSens             // load direction
    ldr x2,[x1]
    add x1,x1,x2
    ldr x2,[x1]
    cmp x2,#0                   // direction inversion
    cset x2,eq
    str x2,[x1]
    b 6f
51:
    cmp x0,#ClientMessage       // code for close window within error
    bne 6f
    adr x0,qOfstEvent
    ldr x5,[x0]
    add x0,x0,x5
    ldr x1,[x0,#+56]            // code message address 
    adr x2,qOfwmDeleteMessage   // equal code window créate ???
    ldr x5,[x2]
    add x2,x2,x5
    ldr x2,[x2]
    cmp x1,x2
    beq 7f                      // yes -> end window 
6:    
    b 3b                        // else events loop
    
7:                              // end program
    mov x0,x28                  // display address
    mov x1,x24
    bl XFreeGC                 // liberation GC1
    mov x0,x28                 // display address
    mov x1,x26
    bl XFreeGC                 // liberation GC2

    mov x0,x28                 // display address
    mov x1,x27                 // window ident
    bl XDestroyWindow
    
    mov x0,x28                 // display address
    bl XCloseDisplay           // connexion X11Server Close
    
    adr x0,qOfszMessFinPgm
    ldr x1,[x0]
    add x0,x0,x1
    bl affichageMess
    b 100f
97:
    adr x0,qOfszMessErreurX11  // X11 error
    ldr x1,[x0]
    add x0,x0,x1
    bl affichageMess
    b 100f
98:                            // erreur creation fenêtre mais ne sert peut être à rien car erreur directe X11
    adr x0,qOfszMessErrfen
    ldr x1,[x0]
    add x0,x0,x1
    bl affichageMess
    b 100f
99:                            // erreur car pas de serveur X
    adr x0,qOfszMessErreur
    ldr x1,[x0]
    add x0,x0,x1
    bl affichageMess
    b 100f


100:                            // fin standard du programme
    mov x0,0                    // code retour
    mov x8,EXIT                 // system call "Exit"
    svc #0
qOfszMessDebutPgm:    .quad szMessDebutPgm - .
qOfszMessFinPgm:      .quad szMessFinPgm - .
qOfszRetourLigne:     .quad szRetourLigne - .
qOfqDisplay:          .quad qDisplay - .
qGris:                .quad 0xF0F0F0F0
qGris1:               .quad 0xFFA0A0A0
qOfszMessErreur:      .quad szMessErreur - .
qOfszMessErrfen:      .quad szMessErrfen - .
qOfszMessErreurX11:   .quad szMessErreurX11 - .
qOfwmDeleteMessage:   .quad wmDeleteMessage - .
qOfszLibDW:           .quad szLibDW - .
qOfszTitreFenRed:     .quad szTitreFenRed - .
qOfszWindowName:      .quad szWindowName - .
qOfstEvent:           .quad stEvent - .
qOfqEcran:            .quad qEcran - .
qOfsBuffer:           .quad sBuffer - .
qOfsbuffer1:          .quad sbuffer1 - .
qOfqKey:              .quad qKey - .
qSaisieMask:          .quad  StructureNotifyMask|ExposureMask|KeyPressMask|ButtonPressMask
qOfszTexte1:          .quad szTexte1 - .

/***********************************************************/
/*   Context Graphic création                            ***/
/***********************************************************/
/* x0 Display   */
/* x1 window */
/* CAUTION : not standard use registers : return ident GC in x24 and x26 !! */
creationGC:
    stp x20,lr,[sp,-16]!        // save  registres
    stp x21,x22,[sp,-16]!       // save  registres
    mov x20,x0
    mov x21,x1
                                // Context Graphic création 
    adr x3,qOfstXGCValues2      // green color in foreground
    ldr x5,[x3]
    add x3,x3,x5                // this parameter is used by XcreateGC
    str x4,[x3,#120]            // store ident font in  offset 120 
    mov x0,x20                  // display address
    mov x1,x21                  // window ident
    mov x2,#GCForeground        // green color in foreground and font
    bl XCreateGC
    cmp x0,#0
    beq 99f                     //  création not OK
    adr x1,qOfptGC1             // store ident graphic context
    ldr x2,[x1]
    add x1,x1,x2
    str x0,[x1]
    mov x26,x0
                                // création contexte 2
    adr x3,qOfstXGCValues       // gris1 color in foreground
    ldr x5,[x3]
    add x3,x3,x5                // this parameter is used by XcreateGC
    mov x0,x20                  // display address
    mov x1,x21                  // window ident
    mov x2,#GCForeground        // gris1  foreground
    bl XCreateGC
    cmp x0,#0
    beq 99f                     //  création not OK
    mov x24,x0
    
    b 100f
    
99:                             // erreur création GC
    adr x0,qOfszMessErreurGC
    ldr x1,[x0]
    add x0,x0,x1
    bl affichageMess
    mov x0,#0
100:
    ldp x21,x22,[sp],16          // restaur des  2 registres
    ldp x20,lr,[sp],16           // restaur des  2 registres
    ret                          // retour adresse lr x30
qGcmask1:             .quad GCForeground|GCBackground
qOfszMessErreurGC:    .quad szMessErrGc - .
qOfptGC1:             .quad ptGC1 - .
qOfstXGCValues:       .quad stXGCValues - .
qOfstXGCValues1:      .quad stXGCValues1 - .
qOfstXGCValues2:      .quad stXGCValues2 - .
/******************************************************************/
/*     animate letters                 */ 
/******************************************************************/
/* x0 contains display address */
/* x1 window ident */
/* x2 graphic context */
/* x3 string address   */
animate:
    stp x20,lr,[sp,-16]!        // save  registres
    stp x21,x22,[sp,-16]!       // save  registres
    mov x20,x3                  // save string address
    mov x21,x0                  // save display
    mov x22,x1                  // save window
    mov x23,x2                  // save GC
    mov x0,x2
                                // erase text in the windows
    mov x0,x21                  // display address
    mov x1,x22                  // window ident
    mov x2,x24                  // graphic context 
    mov x3,#50                  // position x
    mov x4,#100                 // position Y
    mov x5,x20                  // string address
    mov x6,#LGTEXTE1  - 1       // string lenght
    bl XDrawString
    
    mov x0,#LGTEXTE1 -2         // string length
    adr x1,qOfqSens             // load direction
    ldr x2,[x1]
    add x1,x1,x2
    ldr x2,[x1]
    cmp x2,#0                   // test direction
    bne 2f
    
    ldrb w9,[x20,x0]            // last character
    sub x1,x0,#1
1:                              // loop to move character one position
    ldrb w2,[x20,x1]
    strb w2,[x20,x0]
    sub x0,x0,#1
    subs x1,x1,#1
    bge 1b
    add x1,x1,#1
    strb w9,[x20,x1]            // last character -> first character
    b 4f
2:
    ldrb w9,[x20]               // first character
    mov x1,#1
    sub x2,x1,#1
3:                              // loop to move character
    ldrb w3,[x20,x1]
    strb w3,[x20,x2]
    add x2,x2,#1
    add x1,x1,#1
    cmp x1,x0
    ble 3b
    strb w9,[x20,x2]             // first character -> last character

    
4:
                                // display text in the windows
    mov x0,x21                  // display address
    mov x1,x22                  // window ident
    mov x2,x23                  // graphic context 
    mov x3,#50                  // position x
    mov x4,#100                 // position Y
    mov x5,x20                  // string address
    mov x6,#LGTEXTE1  - 1       // string lenght
    bl XDrawString
    mov x0,#20                  // timeout 5s approx.
    bl delay

100:
    ldp x21,x22,[sp],16         // restaur des  2 registres
    ldp x20,lr,[sp],16          // restaur des  2 registres
    ret                         // retour adresse lr x30
qOfqSens:            .quad qSens - .
/******************************************************************/
/*    timeout                                                   */ 
/******************************************************************/
/* x0 contains delay  */
delay:
    stp x1,lr,[sp,-16]!     // save  registres
    ldr x1,qCst50000000
    mul x0,x1,x0
1:
    subs x0,x0,#1
    bge 1b
    
    ldp x1,lr,[sp],16       // restaur registers
    ret
qCst50000000:       .quad 50000000

/********************************************************/
/*        File Include fonctions                        */
/********************************************************/
/* for this file see task include a file in language AArch64 assembly */
.include "../includeARM64.inc"

Action!

PROC DrawWindow(BYTE x,y,len)
  BYTE i

  Position(x,y)
  Put(17)
  FOR i=1 TO len DO Put(18) OD
  Put(5)
  
  Position(x,y+1)
  Put(124)
  Position(x+len+1,y+1)
  Put(124)

  Position(x,y+2)
  Put(26)
  FOR i=1 TO len DO Put(18) OD
  Put(3)
RETURN

PROC RotateLeft(CHAR ARRAY a)
  BYTE i,tmp

  IF a(0)<1 THEN RETURN FI
  
  i=1 tmp=a(i)
  WHILE i<a(0)
  DO
    a(i)=a(i+1)
    i==+1
  OD
  a(a(0))=tmp
RETURN

PROC RotateRight(CHAR ARRAY a)
  BYTE i,tmp

  IF a(0)<1 THEN RETURN FI
  
  i=a(0) tmp=a(i)
  WHILE i>1
  DO
    a(i)=a(i-1)
    i==-1
  OD
  a(1)=tmp
RETURN

PROC Wait(BYTE frames)
  BYTE RTCLOK=$14

  frames==+RTCLOK
  WHILE frames#RTCLOK DO OD
RETURN

PROC Main()
  BYTE
    CH=$02FC, ;Internal hardware value for last key pressed
    CRSINH=$02F0 ;Controls visibility of cursor
  BYTE k,dir=[0]
  CHAR ARRAY text="Hello World! "

  Graphics(0)
  CRSINH=1 ;hide cursor
  Position(4,1)
  PrintE("Press Space to reverse direction")
  Position(11,2)
  Print("Press Esc to exit")
  DrawWindow(12,4,text(0))

  DO
    Position(13,5)
    Print(text)
    k=CH CH=$FF
    IF k=33 THEN dir==!$FF FI
    IF dir THEN
      RotateLeft(text)
    ELSE
      RotateRight(text)
    FI
    Wait(6)
  UNTIL k=28
  OD

  CRSINH=0 ;show cursor
RETURN
Output:

Screenshot from Atari 8-bit computer

ActionScript

//create the text box
var textBox:TextField = new TextField();
addChild(textBox);

var text = "Hello, World! ";
var goingRight = true;

//modify the string and update it in the text box
function animate(e:Event)
{
	if(goingRight)
		text = text.slice(text.length-1,text.length) + text.slice(0, text.length - 1);
	else
		text = text.slice(1) + text.slice(0,1);
	textBox.text = text;
}

//event handler to perform the animation
textBox.addEventListener(Event.ENTER_FRAME, animate);
//event handler to register clicks
textBox.addEventListener(MouseEvent.MOUSE_DOWN, function(){goingRight = !goingRight;});

Ada

Works with: GNAT
Library: GtkAda

animation.adb:

with Gtk.Main;
with Gtk.Handlers;
with Gtk.Label;
with Gtk.Button;
with Gtk.Window;
with Glib.Main;

procedure Animation is
   Scroll_Forwards : Boolean := True;

   package Button_Callbacks is new Gtk.Handlers.Callback
     (Gtk.Button.Gtk_Button_Record);

   package Label_Timeout is new Glib.Main.Generic_Sources
     (Gtk.Label.Gtk_Label);

   package Window_Callbacks is new Gtk.Handlers.Return_Callback
     (Gtk.Window.Gtk_Window_Record, Boolean);

   --  Callback for click event
   procedure On_Button_Click
     (Object : access Gtk.Button.Gtk_Button_Record'Class);

   --  Callback for delete event
   function On_Main_Window_Delete
     (Object : access Gtk.Window.Gtk_Window_Record'Class)
      return   Boolean;

   function Scroll_Text (Data : Gtk.Label.Gtk_Label) return Boolean;

   procedure On_Button_Click
     (Object : access Gtk.Button.Gtk_Button_Record'Class)
   is
      pragma Unreferenced (Object);
   begin
      Scroll_Forwards := not Scroll_Forwards;
   end On_Button_Click;

   function On_Main_Window_Delete
     (Object : access Gtk.Window.Gtk_Window_Record'Class)
      return   Boolean
   is
      pragma Unreferenced (Object);
   begin
      Gtk.Main.Main_Quit;
      return True;
   end On_Main_Window_Delete;

   function Scroll_Text (Data : Gtk.Label.Gtk_Label) return Boolean is
      Text : constant String := Gtk.Label.Get_Text (Data);
   begin
      if Scroll_Forwards then
         Gtk.Label.Set_Text
           (Label => Data,
            Str   => Text (Text'First + 1 .. Text'Last) & Text (Text'First));
      else
         Gtk.Label.Set_Text
           (Label => Data,
            Str   => Text (Text'Last) & Text (Text'First .. Text'Last - 1));
      end if;
      return True;
   end Scroll_Text;

   Main_Window     : Gtk.Window.Gtk_Window;
   Text_Button     : Gtk.Button.Gtk_Button;
   Scrolling_Text  : Gtk.Label.Gtk_Label;
   Timeout_ID      : Glib.Main.G_Source_Id;
   pragma Unreferenced (Timeout_ID);

begin
   Gtk.Main.Init;
   Gtk.Window.Gtk_New (Window => Main_Window);
   Gtk.Label.Gtk_New (Label => Scrolling_Text, Str => "Hello World! ");
   Gtk.Button.Gtk_New (Button => Text_Button);
   Gtk.Button.Add (Container => Text_Button, Widget => Scrolling_Text);
   Button_Callbacks.Connect
     (Widget => Text_Button,
      Name   => "clicked",
      Marsh  => Button_Callbacks.To_Marshaller (On_Button_Click'Access));
   Timeout_ID :=
     Label_Timeout.Timeout_Add
       (Interval => 125,
        Func     => Scroll_Text'Access,
        Data     => Scrolling_Text);
   Gtk.Window.Add (Container => Main_Window, Widget => Text_Button);
   Window_Callbacks.Connect
     (Widget => Main_Window,
      Name   => "delete_event",
      Marsh  => Window_Callbacks.To_Marshaller (On_Main_Window_Delete'Access));
   Gtk.Window.Show_All (Widget => Main_Window);
   Gtk.Main.Main;
end Animation;

Applesoft BASIC

 10  LET T$ = "HELLO WORLD! ":P = 1:L =  LEN (T$):T$ = T$ + T$
 20 D = 1: VTAB  INT (( PEEK (35) -  PEEK (34)) / 2) + 1: FOR R = 1 TO 0 STEP 0
 30  HTAB  INT (( PEEK (33) - L) / 2) + 1: PRINT  MID$ (T$,P,L);
 40 P = P + D: IF P > L THEN P = 1
 50  IF P < 1 THEN P = L
 60 R =  PEEK (49152) < 128: FOR I = 1 TO 5:B =  PEEK (49249) > 127: IF B <  > BO THEN BO = B: IF B = 0 THEN D =  - D
 70  NEXT I,R:R =  PEEK (49168)

ARM Assembly

Works with: as version Raspberry Pi
or android 32 bits with application Termux
/* ARM assembly Raspberry PI or android 32 bits with termux application */
/*  program animLetter.s        */

/* compile with as         */
/* link with gcc and options -lX11 -L/usr/lpp/X11/lib   */

/* REMARK: This program was written for android with the termux app.
 It works very well on raspberry pi but in this case the memory access relocation instructions 
 can be simplified.
*/

 /* REMARK 1 : this program use routines in a include file 
   see task Include a file language arm assembly 
   for the routine affichageMess conversion10 
   see at end of this program the instruction include */
/* for constantes see task include a file in arm assembly */
/************************************/
/* Constantes                       */
/************************************/
.include "../constantes.inc"

/********************************************/
/*Constantes                                */
/********************************************/
.equ STDOUT,              1     @ Linux output console
.equ EXIT,                1     @ Linux syscall
.equ WRITE,               4     @ Linux syscall
/* constantes X11 */
.equ KeyPressed,          2
.equ ButtonPress,         4
.equ MotionNotify,        6
.equ EnterNotify,         7
.equ LeaveNotify,         8
.equ Expose,              12
.equ ClientMessage,       33
.equ KeyPressMask,        1
.equ ButtonPressMask,     4
.equ ButtonReleaseMask,   8
.equ ExposureMask,        1<<15
.equ StructureNotifyMask, 1<<17
.equ EnterWindowMask,     1<<4
.equ LeaveWindowMask,     1<<5 
.equ ConfigureNotify,     22

.equ GCForeground,        1<<2
.equ GCFont,              1<<14

/* constantes program */
.equ WINDOWWIDTH,       400
.equ WINDOWHEIGHT,      300

/*******************************************/
/* DONNEES INITIALISEES                    */
/*******************************************/ 
.data
szWindowName:            .asciz "Windows Raspberry"
szRetourligne:           .asciz  "\n"
szMessDebutPgm:          .asciz "Program start. \n"
szMessErreur:            .asciz "Server X not found.\n"
szMessErrfen:            .asciz "Can not create window.\n"
szMessErreurX11:         .asciz "Error call function X11. \n"
szMessErrGc:             .asciz "Can not create graphics context.\n"
szMessErrFont:           .asciz "Font not found.\n"
szTitreFenRed:           .asciz "Pi"    
szTexte1:                .asciz "Hello world!  "
.equ LGTEXTE1, . - szTexte1
szTexte2:                .asciz "Press q for close window or clic X in system menu."
.equ LGTEXTE2, . - szTexte2
szLibDW:           .asciz "WM_DELETE_WINDOW"    @ special label for correct close error

szfontname: .asciz  "-*-helvetica-bold-*-normal-*-12-*"         @ for font test
szfontname2: .asciz "-*-fixed-*-*-*-*-13-*-*-*-*-*"
.align 4
stXGCValues:       .int 0,0,0xFFA0A0A0,0,0,0,0,0,0,0,0,0,0,0,0,0,0   @ for foreground color gris1
//stXGCValues:       .int 0,0,0x00000000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0   @ for foreground color black
stXGCValues1:      .int 0,0,0x00FFFFFF,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0   @ for foreground color white
stXGCValues2:      .int 0,0,0x0000FF00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0   @ for foreground color green 
/*******************************************/
/* DONNEES NON INITIALISEES                    */
/*******************************************/ 
.bss
.align 4
ptDisplay:          .skip 4      @ pointer display
ptEcranDef:         .skip 4      @ pointer screen default
ptFenetre:          .skip 4      @ pointer window
ptGC:               .skip 4      @ pointer graphic context
ptGC1:              .skip 4      @ pointer graphic context
ptFont:             .skip 4      @ pointer font
key:                .skip 4      @ key code
wmDeleteMessage:    .skip 8      @ ident close message
event:              .skip 400    @ TODO: event size ??
PrpNomFenetre:      .skip 100    @ window name proprety
buffer:             .skip 500 
sbuffer1:             .skip 500
iWhite:             .skip 4      @ rgb code for white pixel
iBlack:             .skip 4      @ rgb code for black pixel
iSens:              .skip 4      @ direction of travel
/**********************************************/
/* -- Code section                            */
/**********************************************/
.text
.global main
iOfWhite:           .int iWhite - .
iOfBlack:           .int iBlack - .
iOfszMessDebutPgm:  .int szMessDebutPgm - .
iOfptDisplay:       .int ptDisplay - .
iOfptEcranDef:      .int ptEcranDef - .
iOfszLibDW:         .int szLibDW - .
main:                           @ entry of program 
    adr r0,iOfszMessDebutPgm   @
    ldr r1,[r0]
    add r0,r1
    bl affichageMess            @ display start message on console linux
    /* attention r6  pointer display*/
    /* attention r8  pointer graphic context 1  */
    /* attention r9 ident window  */
    /* attention r10  pointer graphic context 2  */
    /*****************************/
    /*    OPEN SERVER X11        */
    /*****************************/
    mov r0,#0
    bl XOpenDisplay             @ open X server
    cmp r0,#0                   @ error ?
    beq erreurServeur
    adr r2,iOfptDisplay
    ldr r1,[r2]
    add r1,r2
    str r0,[r1]                 @ store display address 

    mov r6,r0                   @ and in register r6
    ldr r2,[r0,#+132]           @ load default_screen
    adr r1,iOfptEcranDef
    ldr r3,[r1]
    add r1,r3
    str r2,[r1]                 @ store default_screen
    mov r2,r0
    ldr r0,[r2,#+140]           @ load pointer screen list
    ldr r5,[r0,#+52]            @ load value white pixel
    adr r4,iOfWhite             @ and store in memory
    ldr r3,[r4]
    add r4,r3
    str r5,[r4]
    ldr r7,[r0,#+56]            @ load value black pixel
    adr r4,iOfBlack             @ and store in memory
    ldr r5,[r4]
    add r4,r5
    str r7,[r4]
    ldr r4,[r0,#+28]            @ load bits par pixel
    ldr r1,[r0,#+8]             @ load root windows
    /**************************/
    /* CREATE WINDOW          */
    /**************************/
    mov r0,r6                   @ address display
    mov r2,#0                   @ window position X
    mov r3,#0                   @ window position Y
    mov r8,#0                   @ for stack alignement
    push {r8}
    push {r7}                   @  background  = black pixel
    push {r5}                   @  border = white pixel
    mov r8,#2                   @  border size
    push {r8}
    mov r8,#WINDOWHEIGHT        @ height
    push {r8}
    mov r8,#WINDOWWIDTH         @ width
    push {r8}   
    bl XCreateSimpleWindow
    add sp,#24                  @ stack alignement  6 push (4 bytes * 6)
    cmp r0,#0                   @ error ?
    beq erreurF

    adr r1,iOfptFenetre
    ldr r3,[r1]
    add r1,r3
    str r0,[r1]                 @ store window address in memory
    mov r9,r0                   @ and in register r9
    
    /*****************************/
    /* add window property       */
    /*****************************/
    mov r0,r6                   @ display address
    mov r1,r9                   @ window address
    adr r2,iOfszWindowName      @ window name
    ldr r5,[r2]
    add r2,r5
    adr r3,iOfszTitreFenRed     @ window name reduced
    ldr r5,[r3]
    add r3,r5
    mov r4,#0
    push {r4}                   @ parameters not use
    push {r4}
    push {r4}
    push {r4}
    bl XSetStandardProperties
    add sp,sp,#16               @ stack alignement for 4 push
    /**************************************/
    /* for correction window close error  */
    /**************************************/
    mov r0,r6                   @ display address
    adr r1,iOfszLibDW           @ atom address
    ldr r5,[r1]
    add r1,r5
    mov r2,#1                   @ False  créate atom if not exists
    bl XInternAtom
    cmp r0,#0                   @ error X11 ?
    blt erreurX11               @ Modif avril 22 pour android (ble pour raspberry)
    adr r1,iOfwmDeleteMessage  @ recept address
    ldr r5,[r1]
    add r1,r5
    str r0,[r1]
    mov r2,r1                   @ return address
    mov r0,r6                   @ display address
    mov r1,r9                   @ window address
    mov r3,#1                   @ number of protocols
    bl XSetWMProtocols
    cmp r0,#0                   @ error X11 ?
    ble erreurX11
    /***********************************/
    /* load font                       */
    /* remark : see font list X11 on your system */
    /***********************************/
    mov r0,r6                   @ display address
    bl loadFont

    /**********************************/
    /*  create graphic context        */
    /**********************************/
    mov r0,r6                   @ display address
    mov r1,r9                   @ window address
    bl createContextGraphic
    

    /****************************/
    /* modif window background  */
    /****************************/
    mov r0,r6                   @ display address
    mov r1,r9                   @ window address
    ldr r2,iGris1               @ background color
    bl XSetWindowBackground   
    cmp r0,#0                   @ error ?
    ble erreurX11
    
    /***************************/
    /* OUF!! window display    */
    /***************************/
    mov r0,r6                   @ display address
    mov r1,r9                   @ window address
    bl XMapWindow
    
                                @ copy text in buffer
    adr r0,iOfszTexte1          @ string address
    ldr r5,[r0]
    add r0,r5
    adr r1,iOfsbuffer1          @ buffer address
    ldr r5,[r1]
    add r1,r5
    mov r2,#0
1:                              @ loop copy character
    ldrb r3,[r0,r2]
    strb r3,[r1,r2]
    cmp r3,#0
    addne r2,r2,#1
    bne 1b
    
    /****************************/
    /* Autorisations            */
    /****************************/
    mov r0,r6                   @ display address
    mov r1,r9                   @ window address
    ldr r2,iFenetreMask         @ autorisation mask
    bl XSelectInput
    cmp r0,#0                   @ error ?
    ble erreurX11
    
    /****************************/
    /* Events loop              */
    /****************************/
1:
    mov r0,r6                   @ display address
    bl XPending                 @ loading the number of events
    cmp r0,#0
    bne 2f                      @ a event is occurring
    adr r3,iOfsbuffer1
    ldr r5,[r3]
    add r3,r5
    mov r0,r6                   @ display address
    mov r1,r9                   @ window ident
    mov r2,r8                   @ graphic context 
    bl animate                  @ string display and move
    b 1b
2:
    mov r0,r6                   @ display address
    adr r1,iOfevent             @ events address
    ldr r5,[r1]
    add r1,r5
    bl XNextEvent               @ event ?
    adr r0,iOfevent
    ldr r5,[r0]
    add r0,r5
    ldr r0,[r0]                 @ code event
    cmp r0,#KeyPressed          @ key ?
    bne 2f
    adr r0,iOfevent            @ yes read key in buffer
    ldr r5,[r0]
    add r0,r5
    adr r1,iOfbuffer
    ldr r5,[r1]
    add r1,r5
    mov r2,#255
    adr r3,iOfkey
    ldr r5,[r3]
    add r3,r5
    mov r4,#0
    push {r4}                   @ stack alignement
    push {r4}
    bl XLookupString 
    add sp,#8                   @ stack alignement 2 push
    cmp r0,#1                   @ is character key ?
    bne 2f
    adr r0,iOfbuffer           @ yes -> load first buffer character
    ldr r5,[r0]
    add r0,r5
    ldrb r0,[r0]
    cmp r0,#0x71                @ character q for quit
    beq 5f                      @ yes -> end
    b 4f
2:
    /************************************/
    /*  clic mouse button   */
    /************************************/
    cmp r0,#ButtonPress         @ clic mouse buton
    bne 3f
    adr r0,iOfevent
    ldr r5,[r0]
    add r0,r5
    ldr r1,[r0,#+32]            @ position X mouse clic
    ldr r2,[r0,#+36]            @ position Y
    cmp r1,#50                  @ test if position clic is on the screen string approx.
    blt 4f
    cmp r1,#150
    bgt 4f
    cmp r2,#80
    blt 4f
    cmp r2,#105
    bgt 4f
    adr r1,iOfiSens             @ load direction
    ldr r2,[r1]
    add r1,r2
    ldr r2,[r1]
    cmp r2,#0                   @ direction inversion
    moveq r2,#1
    movne r2,#0
    str r2,[r1]
    b 4f
3:
    cmp r0,#ClientMessage       @ code for close window within error
    bne 4f
    adr r0,iOfevent
    ldr r5,[r0]
    add r0,r5
    ldr r1,[r0,#+28]            @ code message address 
    adr r2,iOfwmDeleteMessage   @ equal code window créate ???
    ldr r5,[r2]
    add r2,r5
    ldr r2,[r2]
    cmp r1,r2
    beq 5f                      @ yes -> end window 

4:  @ loop for other event
    b 1b
iOfptFenetre:       .int ptFenetre - .
iOfszWindowName:    .int szWindowName - .
iOfszTitreFenRed:   .int szTitreFenRed - .
    /***********************************/
    /* Close window -> free ressources */
    /***********************************/
5:
    mov r0,r6                  @ display address
    adr r1,iOfptGC
    ldr r5,[r1]
    add r1,r5
    ldr r1,[r1]                @ load context graphic address 
    bl XFreeGC
    mov r0,r6                  @ display address
    adr r1,iOfptGC1
    ldr r5,[r1]
    add r1,r5
    ldr r1,[r1]                @ load context graphic address 
    bl XFreeGC
    cmp r0,#0
    blt erreurX11
    mov r0,r6                  @ display address 
    mov r1,r9                  @ window address
    bl XDestroyWindow
    cmp r0,#0
    blt erreurX11
    mov r0,r6                  @ display address
    bl XCloseDisplay
    cmp r0,#0
    blt erreurX11
    mov r0,#0                  @ return code OK
    b 100f

erreurF:   @ create error window but possible not necessary. Display error by server
    adr r1,iOfszMessErrfen
    ldr r5,[r1]
    add r1,r5
    bl   displayError
    mov r0,#1                  @ return error code
    b 100f
erreurGC:                      @ error create graphic context
    adr r1,iOfszMessErrGc
    ldr r5,[r1]
    add r1,r5
    bl   displayError
    mov r0,#1
    b 100f
erreurX11:    @ erreur X11
    adr r1,iOfszMessErreurX11
    ldr r5,[r1]
    add r1,r5
    bl   displayError
    mov r0,#1
    b 100f
erreurServeur:                 @ error no found X11 server see doc putty and Xming
    adr r1,iOfszMessErreur
    ldr r5,[r1]
    add r1,r5
    bl   displayError
    mov r0,#1
    b 100f

100:                           @ standard end of the program 
    mov r7, #EXIT
    svc 0 

iOfevent:           .int event - .
iOfbuffer:          .int buffer - .
iOfsbuffer1:        .int sbuffer1 - .
iOfkey:             .int key - .
iOfszMessErreurX11: .int szMessErreurX11 - .
iOfszMessErreur:    .int szMessErreur - .
iOfszMessErrfen:    .int szMessErrfen - .
iOfszTexte1:        .int szTexte1 - .
iOfszTexte2:        .int szTexte2 - .
iOfPrpNomFenetre:   .int PrpNomFenetre - .
iOfwmDeleteMessage: .int wmDeleteMessage - .

iFenetreMask:        .int  KeyPressMask|ButtonPressMask|StructureNotifyMask
iGris1:              .int 0xFFA0A0A0
iOfiSens:            .int iSens - .
/******************************************************************/
/*    load font                                                  */ 
/******************************************************************/
/* r0 contains display  */
loadFont:
    push {r1-r2,lr}           @ save  registers 
    adr r1,iOfszfontname       @ font name
    ldr r2,[r1]
    add r1,r2
    bl XLoadQueryFont
    cmp r0,#0
    beq 99f                    @ font not find
    adr r1,iOfptFont
    ldr r2,[r1]
    add r1,r2
    str r0,[r1]
    mov r0,#0
    b 100f
99:
    adr r1,iOfszMessErrFont
    ldr r5,[r1]
    add r1,r5
    bl   displayError
    mov r0,#1
100:
    pop {r1-r2,pc}             @ restaur registers
iOfszfontname:          .int szfontname2 - .
iOfptFont:              .int ptFont - .
iOfszMessErrFont:       .int szMessErrFont - .
/******************************************************************/
/*    Context Graphic création                                                  */ 
/******************************************************************/
/* r0 contains display  */
/* r1 window address */
/* REMARKS : no standard use registers  return GC1 in r8 and GC2 in r10 */ 
createContextGraphic:
    push {r1-r7,lr}             @ save  registers 
    /**********************************/
    /*  create graphic context        */
    /**********************************/
    mov r6,r0                   @ save display address
    mov r7,r1                   @ save window address
    adr r3,iOfptFont            @ font pointer
    ldr r1,[r3]
    add r3,r1
    ldr r3,[r3]
    ldr r4,[r3,#4]              @ load font ident
    adr r3,iOfstXGCValues2      @ green color in foreground
    ldr r5,[r3]
    add r3,r5                   @ this parameter is used by XcreateGC
    str r4,[r3,#60]             @ store ident font in  offset 60 
    mov r0,r4
    mov r0,r6                   @ display address
    mov r1,r7                   @ window address
    mov r2,#GCForeground|GCFont @  @ green color in foreground and font
    bl XCreateGC
    cmp r0,#0                   @ error ?
    beq 99f
    adr r1,iOfptGC
    ldr r5,[r1]
    add r1,r5
    str r0,[r1]                 @ store address graphic context
    mov r8,r0                   @ and in r8
    /**********************************/
    /*  create 2 graphic context        */
    /**********************************/
    mov r0,r6                   @ display address
    mov r1,r7                   @ window address
    mov r2,#GCForeground        @ red color in Foreground
    adr r3,iOfstXGCValues
    ldr r5,[r3]
    add r3,r5
    bl XCreateGC
    cmp r0,#0                   @ error ?
    beq erreurGC
    adr r1,iOfptGC1
    ldr r5,[r1]
    add r1,r5
    str r0,[r1]                 @ store address graphic context
    mov r10,r0                  @ and in r10
    b 100f
99:                            @ create error
    adr r1,iOfszMessErrGc
    ldr r5,[r1]
    add r1,r5
    bl   displayError
    mov r0,#1
100:
    pop {r1-r7,pc}             @ restaur registers
iOfszMessErrGc:     .int szMessErrGc - .
iOfptGC:            .int ptGC - .
iOfptGC1:           .int ptGC1 - .
iOfstXGCValues:     .int stXGCValues - .
iOfstXGCValues1:     .int stXGCValues1 - .
iOfstXGCValues2:     .int stXGCValues2 - .
/******************************************************************/
/*    animation                                                   */ 
/******************************************************************/
/* r0 contains display  */
/* r1 contains window ident */
/* r2 contains context graphic */
/* r3 string address   */
animate:
    push {r2-r9,lr}            @ save  registers 
    mov r5,r3                   @ save string address
    mov r6,r0                   @ save display
    mov r7,r1                   @ save window
    mov r8,r2                   @ save GC
                                @ erase text in the windows
    mov r0,r6                   @ display address
    mov r1,r7                   @ window ident
    mov r2,r10                  @ graphic context 
    mov r3,#50                  @ position x
    mov r4,#LGTEXTE1  - 1       @ string lenght
    push {r4}                   @ stack alignement
    push {r4}                   @ to stack parameter

    push {r5}                   @ string address
    mov r4,#100                 @ position y
    push {r4}
    bl XDrawString
    add sp,sp,#16               @ stack alignement (4 push)

    mov r0,#LGTEXTE1 -2         @ string length
    adr r1,iOfiSens             @ load direction
    ldr r2,[r1]
    add r1,r2
    ldr r2,[r1]
    cmp r2,#0                   @ test direction
    bne 2f
    
    ldrb r9,[r5,r0]             @ last character
    sub r1,r0,#1
1:                              @ loop to move character one position
    ldrb r2,[r5,r1]
    strb r2,[r5,r0]
    sub r0,r0,#1
    subs r1,r1,#1
    bge 1b
    add r1,r1,#1
    strb r9,[r5,r1]             @ last character -> first character
    b 4f
2:
    ldrb r9,[r5]                @ first character
    mov r1,#1
    sub r2,r1,#1
3:                              @ loop to move character
    ldrb r3,[r5,r1]
    strb r3,[r5,r2]
    add r2,r2,#1
    add r1,r1,#1
    cmp r1,r0
    ble 3b
    strb r9,[r5,r2]             @ first character -> last character
    
4:
                                @ write text in the windows
    mov r0,r6                   @ display address
    mov r1,r7                   @ window ident
    mov r2,r8                   @ graphic context 
    mov r3,#50                  @ position x
    mov r4,#LGTEXTE1  - 1       @ string lenght
    push {r4}                   @ stack alignement
    push {r4}                   @ to stack parameter
    push {r5}                   @ string address
    mov r4,#100                 @ position y
    push {r4}
    bl XDrawString
    add sp,sp,#16               @ stack alignement (4 push)
    mov r0,#20                  @ timeout 5s approx.
    bl delay

100:
    pop {r2-r9,pc}             @ restaur registers
/******************************************************************/
/*    timeout                                                   */ 
/******************************************************************/
/* r0 contains delay  */

delay:
    push {r1,lr}           @ save  registers 
    ldr r1,iCst30000000
    mul r0,r1,r0
1:
    subs r0,r0,#1
    bge 1b
100:
    pop {r1,pc}             @ restaur registers
iCst30000000:       .int 30000000
/***************************************************/
/*      ROUTINES INCLUDE                 */
/***************************************************/
.include "../affichage.inc"

AutoHotkey

SetTimer, Animate ; Timer runs every 250 ms.
String := "Hello World "
Gui, Add, Text, vS gRev, %String%
Gui, +AlwaysOnTop -SysMenu
Gui, Show
Return

Animate:
	String := (!Reverse) ? (SubStr(String, 0) . Substr(String, 1, StrLen(String)-1)) : (SubStr(String, 2) . SubStr(String, 1, 1))
	GuiControl,,S, %String%
return

Rev: ; Runs whenever user clicks on the text control
	Reverse := !Reverse
return

BASIC

BASIC256

str$="Hello, World! "
direction=0	#value of 0: to the right, 1: to the left.
tlx=10		#Top left x
tly=10		#Top left y
fastgraphics
font "Arial",20,100	#The Font configuration (Font face, size, weight)

main:
while clickb=0
	if direction=0 then
		str$=RIGHT(str$,1) + LEFT(str$,length(str$)-1)
	else
		str$=MID(str$,2,length(str$)-1)+LEFT(str$,1)
	end if
	refresh
	clg
	color red
	text tlx,tly,str$
	pause .1
end while
	#Note: textheight() and textwidth() depends on the latest configuration of the FONT command.
if clickb=1 and clickx>=tlx and clickx<=tlx+textwidth(str$) and clicky>=tly and clicky<=tly+textheight() then
	direction=NOT direction
end if
clickclear
goto main

BBC BASIC

      VDU 23,22,212;40;16,32,16,128
      txt$ = "Hello World! "
      direction% = TRUE
      ON MOUSE direction% = NOT direction% : RETURN
      OFF
      REPEAT
        CLS
        PRINT txt$;
        IF direction% THEN
          txt$ = RIGHT$(txt$) + LEFT$(txt$)
        ELSE
          txt$ = MID$(txt$,2) + LEFT$(txt$,1)
        ENDIF
        WAIT 20
      UNTIL FALSE

C

Library: GTK

(NB: implicitly, through GTK, it uses also Pango library)

#include <stdlib.h>
#include <string.h>
#include <gtk/gtk.h>

const gchar *hello = "Hello World! ";
gint direction = -1;
gint cx=0;
gint slen=0;

GtkLabel *label;

void change_dir(GtkLayout *o, gpointer d)
{
  direction = -direction;
}

gchar *rotateby(const gchar *t, gint q, gint l)
{
  gint i, cl = l, j;
  gchar *r = malloc(l+1);
  for(i=q, j=0; cl > 0; cl--, i = (i + 1)%l, j++)
    r[j] = t[i];
  r[l] = 0;
  return r;
}

gboolean scroll_it(gpointer data)
{
  if ( direction > 0 )
    cx = (cx + 1) % slen;
  else
    cx = (cx + slen - 1 ) % slen;
  gchar *scrolled = rotateby(hello, cx, slen);
  gtk_label_set_text(label, scrolled);
  free(scrolled);
  return TRUE;
}


int main(int argc, char **argv)
{
  GtkWidget *win;
  GtkButton *button;
  PangoFontDescription *pd;

  gtk_init(&argc, &argv);
  win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title(GTK_WINDOW(win), "Basic Animation");
  g_signal_connect(G_OBJECT(win), "delete-event", gtk_main_quit, NULL);

  label = (GtkLabel *)gtk_label_new(hello);

  // since we shift a whole character per time, it's better to use
  // a monospace font, so that the shifting seems done at the same pace
  pd = pango_font_description_new();
  pango_font_description_set_family(pd, "monospace");
  gtk_widget_modify_font(GTK_WIDGET(label), pd);

  button = (GtkButton *)gtk_button_new();
  gtk_container_add(GTK_CONTAINER(button), GTK_WIDGET(label));

  gtk_container_add(GTK_CONTAINER(win), GTK_WIDGET(button));
  g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(change_dir), NULL);

  slen = strlen(hello);

  g_timeout_add(125, scroll_it, NULL);
  
  gtk_widget_show_all(GTK_WIDGET(win));
  gtk_main();
  return 0;
}

C#

using System;
using System.Drawing;
using System.Windows.Forms;

namespace BasicAnimation
{
  class BasicAnimationForm : Form
  {
    bool isReverseDirection;
    Label textLabel;
    Timer timer;

    internal BasicAnimationForm()
    {
      this.Size = new Size(150, 75);
      this.Text = "Basic Animation";

      textLabel = new Label();
      textLabel.Text = "Hello World! ";
      textLabel.Location = new Point(3,3);
      textLabel.AutoSize = true;
      textLabel.Click += new EventHandler(textLabel_OnClick);
      this.Controls.Add(textLabel);

      timer = new Timer();
      timer.Interval = 500;
      timer.Tick += new EventHandler(timer_OnTick);
      timer.Enabled = true;

      isReverseDirection = false;
    }

    private void timer_OnTick(object sender, EventArgs e)
    {
      string oldText = textLabel.Text, newText;
      if(isReverseDirection)
        newText = oldText.Substring(1, oldText.Length - 1) + oldText.Substring(0, 1);
      else
        newText = oldText.Substring(oldText.Length - 1, 1) + oldText.Substring(0, oldText.Length - 1);
      textLabel.Text = newText;
    }

    private void textLabel_OnClick(object sender, EventArgs e)
    {
      isReverseDirection = !isReverseDirection;
    }
  }

   class Program
   {
      static void Main()
      {
	Application.Run(new BasicAnimationForm());
      }
   }
}

C++

Library: Qt

Contents of animationwidget.cpp:

#include "animationwidget.h"

#include <QLabel>
#include <QTimer>
#include <QVBoxLayout>

#include <algorithm>

AnimationWidget::AnimationWidget(QWidget *parent) : QWidget(parent) {
    setWindowTitle(tr("Animation"));
    QFont font("Courier", 24);
    QLabel* label = new QLabel("Hello World! ");
    label->setFont(font);
    QVBoxLayout* layout = new QVBoxLayout(this);
    layout->addWidget(label);
    QTimer* timer = new QTimer(this);
    connect(timer, &QTimer::timeout, this, [label,this]() {
        QString text = label->text();
        std::rotate(text.begin(), text.begin() + (right_ ? text.length() - 1 : 1), text.end());
        label->setText(text);
    });
    timer->start(200);
}

void AnimationWidget::mousePressEvent(QMouseEvent*) {
    right_ = !right_;
}

Contents of animationwidget.h:

#ifndef ANIMATIONWIDGET_H
#define ANIMATIONWIDGET_H

#include <QWidget>

class AnimationWidget : public QWidget {
    Q_OBJECT
public:
    AnimationWidget(QWidget *parent = nullptr);
protected:
    void mousePressEvent(QMouseEvent *event) override;
private:
    bool right_ = true;
};

#endif // ANIMATIONWIDGET_H

Contents of main.cpp:

#include "animationwidget.h"

#include <QApplication>

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);
    AnimationWidget w;
    w.show();
    return a.exec();
}

Ceylon

module.ceylon

native("jvm")
module animation "1.0.0" {
	import java.desktop "8";
}

run.ceylon

import javax.swing {
	JFrame,
	JLabel,
	Timer
}
import java.awt.event {
	MouseAdapter,
	MouseEvent,
	ActionListener,
	ActionEvent
}

shared void run() {
 	value initialText = "Hello World! ";
 	value label = JLabel(initialText);
	variable value forward = true;
	label.addMouseListener(object extends MouseAdapter() {
        	mouseClicked(MouseEvent? mouseEvent) => 
                	forward = !forward;
	});

	Timer(1k, object satisfies ActionListener {
		shared actual void actionPerformed(ActionEvent? actionEvent) {
			String left;
			String right;
			if(forward) {
				left = label.text.last?.string else "?";
				right = "".join(label.text.exceptLast);
			} else {
				left = label.text.rest;
				right = label.text.first?.string else "?";
			}
			label.text = left + right;
		}
	}).start();
	
	value frame = JFrame();
	frame.defaultCloseOperation = JFrame.\iEXIT_ON_CLOSE;
	frame.title = "Rosetta Code Animation Example";
	frame.add(label);
	frame.pack();
	frame.visible = true;
}

Clojure

Clojure is a JVM language so this example uses Swing, and illustrates Clojure's platform integration.

(import '[javax.swing JFrame JLabel])
(import '[java.awt.event MouseAdapter])

(def text "Hello World! ")
(def text-ct (count text))
(def rotations
  (vec
    (take text-ct
      (map #(apply str %)
        (partition text-ct 1 (cycle text))))))

(def pos (atom 0))  ;position in rotations vector being displayed
(def dir (atom 1))  ;direction of next position (-1 or 1)

(def label (JLabel. text))

(.addMouseListener label
  (proxy [MouseAdapter] []
    (mouseClicked [evt] (swap! dir -))))

(defn animator []
  (while true
    (Thread/sleep 100)
    (swap! pos #(-> % (+ @dir) (mod text-ct)))
    (.setText label (rotations @pos))))

(doto (JFrame.)
  (.add label)
  (.pack)
  (.setDefaultCloseOperation JFrame/EXIT_ON_CLOSE)
  (.setVisible true))
  
(future-call animator)  ;simple way to run animator on a separate thread

Commodore BASIC

There is no window management in the stock version of BASIC V2 on the Commodore 64 or VIC 20. This example below is generic enough to use on all Commodore variants. Note: Asterisks (*) may be replaced with Commodore graphics characters for a more decorative "window".

1 rem rosetta code
2 rem task: animation
10 t$="hello world! ":d=-1
20 print chr$(147);:rem clear screen
30 print "*****************"
35 print "*               *"
40 print "*               *"
45 print "*               *"
50 print "*****************"
60 print "{HOME}{DOWN}{DOWN}{RIGHT}{RIGHT}"t$
70 get k$
75 if k$=" " then d=not d
80 on (abs(d))+1 gosub 1000,1010
90 if k$="q" then print chr$(147):end
95 rem 
100 rem between line 80 and line 999
110 rem check for other user input
120 rem and process accordingly
130 rem
999 for i=1 to 100:next:goto 60
1000 t$=right$(t$,len(t$)-1)+left$(t$,1):return
1010 t$=right$(t$,1)+left$(t$,len(t$)-1):return

Common Lisp

The ltk package provides a lisp interface to Tk for creating graphical interfaces. Assuming ltk has been installed somewhere the following will work as per the Tcl example.

Library: Tk
(use-package :ltk)
 
(defparameter *message* "Hello World! ")
(defparameter *direction* :left)
(defun animate (label)
  (let* ((n (length *message*))
         (i (if (eq *direction* :left) 0 (1- n)))
         (c (char *message* i)))
    (if (eq *direction* :left)
        (setq *message* (concatenate 'string
				     (subseq *message* 1 n) 
				     (list c)))
	(setq *message* (concatenate 'string (list c) 
				     (subseq *message* 0 (1- n)))))
    (setf (ltk:text label) *message*)
    (ltk:after 125 (lambda () (animate label)))))
 
(defun basic-animation ()
  (ltk:with-ltk ()
      (let* ((label (make-instance 'label 
                                   :font "Courier 14")))
        (setf (text label) *message*)
        (ltk:bind label "<Button-1>"
                  (lambda (event)
                    (declare (ignore event))
                    (cond
                     ((eq *direction* :left) (setq *direction* :right))
                     ((eq *direction* :right) (setq *direction* :left)))))
        (ltk:pack label)
        (animate label)
        (ltk:mainloop))))
 
(basic-animation)

D

Library: QD
uses
Library: SDL
Library: SDL_ttf
Library: tools
module test26;

import qd, SDL_ttf, tools.time;

void main() {
  screen(320, 200);
  auto last = sec();
  string text = "Hello World! ";
  auto speed = 0.2;
  int dir = true;
  while (true) {
    cls;
    print(10, 10, Bottom|Right, text);
    if (sec() - last > speed) {
      last = sec();
      if (dir == 0) text = text[$-1] ~ text[0 .. $-1];
      else text = text[1 .. $] ~ text[0];
    }
    flip; events;
    if (mouse.clicked
      && mouse.pos in display.select(10, 10, 100, 20)
    ) dir = !dir;
  }
}

Dart

Flutter

Library: Flutter

view it on dartpad!

import 'package:flutter/material.dart';
import 'dart:async' show Timer;

void main() {
  
  var timer = const Duration( milliseconds: 75 ); // How often to update i.e. how fast the animation is
  
  runApp(MaterialApp (
    home: Scaffold (
      body: Center (
        child: AnimatedText( timer )
      )
    )
  ));
}

class AnimatedText extends StatefulWidget {
  
  final Duration period; // Time period for update
  
  AnimatedText( this.period );
  @override
  _AnimatedText createState() => _AnimatedText( period );
}


class _AnimatedText extends State<AnimatedText> {
  
  bool forward = true; // Text should go forward?
  
  Timer timer; // Timer Objects allow us to do things based on a period of time
  // We want to get an array of characters, but Dart does not have a char type
  // Below is the equivalent code
  var _text =  'Hello World!      '.runes // .runes gives us the unicode number of each character
    .map( (code) => String.fromCharCode(code) ) // Map all these codes to Strings containing the single character
    .toList(); // Conver to a List
  
  _AnimatedText( Duration period ){
    timer = Timer.periodic( period , (_){
      setState((){ // Set state forces the gui elements to be redrawn
        shiftText();
        
      });  
    });
  }
  
  String get text => _text.join(''); // Getter, joins the list of chars into a string
  
  void shiftText() {
    if (forward) { // If we should go forward
      var last = _text.removeLast(); // Remove the last char

      _text.insert( 0, last); // Insert it at the front
      
    } else { // If we should go backward
      var first = _text.removeAt(0); // Remove the first char
      
      _text.insert( _text.length, first ); // Insert it at the end
    }
    
 }
  
  @override
  Widget build(BuildContext context) {
    return GestureDetector( // GestureDetector lets us capture events
      onTap: () => forward = !forward, // on Tap (Click in browser) invert the forward bool
      child: Text(
        text, // Call the text getter to get the shifted string
        style: TextStyle( // Styling
          fontSize: 50,
          color: Colors.grey[600]
        )
      )
    
    );   
  }
    
}


HTML DOM

Library: Html Dom
import 'dart:html';
import 'dart:async';

const frameTime = const Duration(milliseconds: 100);

void main() {
  String text = "Hello World! ";
  bool rotateRight = true;

  Element writeHere =
      querySelector('#output'); // assumes you have a pre with that ID
  writeHere.onClick.listen((event) => rotateRight = !rotateRight);

  new Timer.periodic(frameTime, (_) {
    text = changeText(text, rotateRight);
    writeHere.text = text;
  });
}

String changeText(extt, rotateRight) {
  if (rotateRight) {
    return extt.substring(extt.length - 1) + extt.substring(0, extt.length - 1);
  } else {
    return extt.substring(1) + extt.substring(0, 1);
  }
}

Delphi

Library: Vcl.Forms
unit Main;

interface

uses
  Winapi.Windows, System.SysUtils, System.Classes, Vcl.Controls, Vcl.Forms,
  Vcl.StdCtrls, Vcl.ExtCtrls;

type
  TForm1 = class(TForm)
    lblAniText: TLabel;
    tmrAniFrame: TTimer;
    procedure lblAniTextClick(Sender: TObject);
    procedure tmrAniFrameTimer(Sender: TObject);
  end;

var
  Form1: TForm1;
  Reverse: boolean = false;

implementation

{$R *.dfm}

procedure TForm1.lblAniTextClick(Sender: TObject);
begin
  Reverse := not Reverse;
end;

function Shift(text: string; direction: boolean): string;
begin
  if direction then
    result := text[text.Length] + text.Substring(0, text.Length - 1)
  else
    result := text.Substring(1, text.Length) + text[1];
end;

procedure TForm1.tmrAniFrameTimer(Sender: TObject);
begin
  with lblAniText do
    Caption := Shift(Caption, Reverse);
end;
end.

Form resource:

object Form1: TForm1
  ClientHeight = 132
  ClientWidth = 621
  object lblAniText: TLabel
    Align = alClient
    Alignment = taCenter
    Caption = 'Hello World! '
    Font.Height = -96
    OnClick = lblAniTextClick
  end
  object tmrAniFrame: TTimer
    Interval = 200
    OnTimer = tmrAniFrameTimer
  end
end

E

Works with: E-on-Java
(Java Swing; tested on Mac OS X 10.5.7)
# State
var text := "Hello World! "
var leftward := false

# Window
def w := <swing:makeJFrame>("RC: Basic Animation")

# Text in window
w.setContentPane(def l := <swing:makeJLabel>(text))
l.setOpaque(true) # repaints badly if not set!
l.addMouseListener(def mouseListener {
    to mouseClicked(_) {
        leftward := !leftward
    }
    match _ {}
})

# Animation
def anim := timer.every(100, fn _ { # milliseconds
    def s := text.size()
    l.setText(text := if (leftward) {
        text(1, s) + text(0, 1)
    } else {
        text(s - 1, s) + text(0, s - 1)
    })
})

# Set up window shape and close behavior
w.pack()
w.setLocationRelativeTo(null)
w.addWindowListener(def windowListener { 
    to windowClosing(_) { anim.stop() } 
    match _ {}
})

# Start everything
w.show()
anim.start()

Text-only version (no Java dependency; no clicking, use reverse() and stop() to control):

def [reverse, stop] := {
    var text := "Hello World! "
    var leftward := false

    def anim := timer.every(100, fn _ { # milliseconds
        def s := text.size()
        text := if (leftward) {
            text(1, s) + text(0, 1)
        } else {
            text(s - 1, s) + text(0, s - 1)
        }
        print("\b" * s, text)
    })
    print("\n", text)
    anim.start()
    [def _() { leftward := !leftward; null }, anim.stop]
}

EasyLang

s$ = "Hello world! "
textsize 16
lg = len s$
on timer
   color 333
   move 10 20
   rect 80 20
   color 999
   move 12 24
   text s$
   if forw = 0
      s$ = substr s$ lg 1 & substr s$ 1 (lg - 1)
   else
      s$ = substr s$ 2 (lg - 1) & substr s$ 1 1
   .
   timer 0.2
.
on mouse_down
   if mouse_x > 10 and mouse_x < 90
      if mouse_y > 20 and mouse_y < 40
         forw = 1 - forw
      .
   .
.
timer 0


Elm

module Main exposing (..)

import Browser
import Html exposing (Html, div, pre, text)
import Html.Events exposing (onClick)
import Time


message : String
message =
    "Hello World! "


main : Program () Model Msg
main =
    Browser.element
        { init = init
        , update = update
        , subscriptions = subscriptions
        , view = view
        }



-- MODEL


type Direction
    = Forwards
    | Backwards


type alias Model =
    { time : Int
    , direction : Direction
    }


init : () -> ( Model, Cmd Msg )
init _ =
    ( { time = 0, direction = Forwards }, Cmd.none )



-- UPDATE


type Msg
    = Tick
    | SwitchDirection


switchDirection : Direction -> Direction
switchDirection d =
    case d of
        Forwards ->
            Backwards

        Backwards ->
            Forwards


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    let
        nextTime =
            case model.direction of
                Forwards ->
                    model.time - 1

                Backwards ->
                    model.time + 1
    in
    case msg of
        Tick ->
            ( { model | time = modBy (String.length message) nextTime }, Cmd.none )

        SwitchDirection ->
            ( { model | direction = switchDirection model.direction }, Cmd.none )



-- SUBSCRIPTIONS


subscriptions : Model -> Sub Msg
subscriptions _ =
    Time.every 200 (\_ -> Tick)



-- VIEW


rotate : String -> Int -> String
rotate m x =
    let
        end =
            String.slice x (String.length m) m

        beginning =
            String.slice 0 x m
    in
    end ++ beginning


view : Model -> Html Msg
view model =
    div []
        [ pre [ onClick SwitchDirection ]
            [ text <| rotate message model.time ]
        ]

Emacs Lisp

(defun animation-start ()
  (interactive)
  (make-thread
   (lambda () 
     (let ()
       (setq **animation-state** (make-hash-table))
       (puthash 'offset 0 **animation-state**)
       (puthash 'sleep-cnt 5 **animation-state**)
       (puthash 'len 20 **animation-state**)
       (puthash 'buffer (make-string (gethash 'len **animation-state**) ? )
		**animation-state**)
       (puthash 'text "Hello World!" **animation-state**)
       (puthash 'forward 't **animation-state**)
       
       (switch-to-buffer-other-window "**animation**")
       (erase-buffer)
       (insert "\n")
       (insert-button
	"Revert Direction"
	'action (lambda (x)
		  (let ((forward (gethash 'forward **animation-state**)))
		    (puthash 'forward (not forward) **animation-state**)))
	'follow-link 't)
       
       (while 't
	 (let ((offset (gethash 'offset **animation-state**))
	       (len (gethash 'len **animation-state**))
	       (txt (gethash 'text **animation-state**))
	       (buff (gethash 'buffer **animation-state**))
	       (is-forward (gethash 'forward **animation-state**)))
	   
	   (fillarray buff ? )
	   (seq-map-indexed (lambda (ch idx)
			      (aset buff (% (+ offset idx) len) ch))
			    txt)
	   (puthash 'buffer buff **animation-state**)
	   (if is-forward
	       (puthash 'offset (1+ offset) **animation-state**)
	     (puthash 'offset (% (+ len (1- offset)) len) **animation-state**))
	   
	   ;;(erase-buffer)
	   (beginning-of-buffer)
	   (delete-region (line-beginning-position) (line-end-position))
	   (insert buff)
	   ;;(message buff)
	   )
	 (sleep-for 0 500)
	 )  
       )
     )
   "animation thread")
  )

(animation-start)

F#

open System.Windows

let str = "Hello world! "
let mutable i = 0
let mutable d = 1

[<System.STAThread>]
do
  let button = Controls.Button()
  button.Click.Add(fun _ -> d <- str.Length - d)
  let update _ =
    i <- (i + d) % str.Length
    button.Content <- str.[i..] + str.[..i-1]
  Media.CompositionTarget.Rendering.Add update
  (Application()).Run(Window(Content=button)) |> ignore

Factor

USING: accessors timers calendar kernel models sequences ui
ui.gadgets ui.gadgets.labels ui.gestures ;
FROM: models => change-model ;
IN: rosetta.animation
 
CONSTANT: sentence "Hello World! "
 
TUPLE: animated-label < label-control reversed alarm ;
: <animated-label> ( model -- <animated-model> )
    sentence animated-label new-label swap >>model 
    monospace-font >>font ;
: update-string ( str reverse -- str )
    [ unclip-last prefix ] [ unclip suffix ] if ;
: update-model ( model reversed? -- )
    [ update-string ] curry change-model ;
 
animated-label  
    H{ 
        { T{ button-down } [ [ not ] change-reversed drop ] }
     } set-gestures
 
M: animated-label graft*
  [ [ [ model>> ] [ reversed>> ] bi update-model ] curry 400 milliseconds every ] keep
  alarm<< ;
M: animated-label ungraft*
    alarm>> stop-timer ;
: main ( -- )
   [ sentence <model> <animated-label> "Rosetta" open-window ] with-ui ;
 
MAIN: main

Fantom

using concurrent
using fwt
using gfx

const class RotateString : Actor
{
  new make (Label label) : super (ActorPool ())
  {
    Actor.locals["rotate-label"] = label
    Actor.locals["rotate-string"] = label.text
    Actor.locals["direction"] = "forward"
    sendLater (1sec, "update")
  }

  // responsible for calling appropriate methods to process each message
  override Obj? receive (Obj? msg)
  {
    switch (msg)
    {
      case "update":
        Desktop.callAsync |->| { update }  // make sure we update GUI in event thread
        sendLater (1sec, "update")
      case "reverse":
        Desktop.callAsync |->| { reverse }
    }
    
    return null
  }

  // change the stored string indicating the direction to rotate
  Void reverse ()
  {
    Actor.locals["direction"] = 
        (Actor.locals["direction"] == "forward" ? "backward" : "forward")
  }

  // update the text on the label according to the stored direction
  Void update ()
  {
    label := Actor.locals["rotate-label"] as Label
    str := Actor.locals["rotate-string"] as Str
    if (label != null)
    {
      newStr := ""
      if (Actor.locals["direction"] == "forward")
        newStr = str[1..-1] + str[0].toChar
      else
        newStr = str[-1].toChar + str[0..<-1]
      label.text = newStr
      Actor.locals["rotate-string"] = newStr
    }
  }
}

class Animate
{
  public static Void main ()
  {
    label := Label 
    { 
      text = "Hello world! " 
      halign = Halign.center
    }
    ticker := RotateString (label)
    label.onMouseDown.add |Event e|
    {
      ticker.send ("reverse")
    }
    Window
    {
      title = "Animate"
      label,
    }.open
  }
}

FBSL

#INCLUDE <Include\Windows.inc>

FBSLSETTEXT(ME, "Hello world! ")
RESIZE(ME, 0, 0, 220, 0)
CENTER(ME)
SHOW(ME)
SetTimer(ME, 1000, 100, NULL)

BEGIN EVENTS
	STATIC bForward AS BOOLEAN = TRUE
	IF CBMSG = WM_TIMER THEN
		Marquee(bForward)
		RETURN 0
	ELSEIF CBMSG = WM_NCLBUTTONDOWN THEN
		IF CBWPARAM = HTCAPTION THEN bForward = NOT bForward
	ELSEIF CBMSG = WM_CLOSE THEN
		KillTimer(ME, 1000)
	END IF
END EVENTS

SUB Marquee(BYVAL forward AS BOOLEAN)
	STATIC caption AS STRING = FBSLGETTEXT(ME)
	STATIC length AS INTEGER = STRLEN(caption)
	IF forward THEN
		caption = RIGHT(caption, 1) & LEFT(caption, length - 1)
	ELSE
		caption = MID(caption, 2) & LEFT(caption, 1)
	END IF
	FBSLSETTEXT(ME, caption)
END SUB

FreeBASIC

Any mouse button on the text will change the direction.

To exit, push the window's closing cross. (255 + 107 is the combination that is passed to INKEY$ by that button.)

DIM C AS STRING = "Hello World! ", SIZE AS USHORT = LEN(C)
DIM DIRECTION AS BYTE = 0
DIM AS INTEGER X, Y, BTNS
DIM HELD AS BYTE = 0

SCREEN 19

DO
 LOCATE 1, 1
 PRINT C

 GETMOUSE X, Y, , BTNS

 IF BTNS <> 0 AND HELD = 0 THEN
  'remember if it was pressed, to not react every frame
  HELD = 1
  IF X >= 0 AND X < SIZE * 8 AND Y >= 0 AND Y < 16 THEN
   DIRECTION = 1 - DIRECTION
  END IF
 ELSE
  HELD = 0
 END IF

 IF INKEY = CHR(255) + CHR(107) THEN EXIT DO

 IF DIRECTION = 0 THEN
  C = RIGHT(C, 1) + LEFT(C, SIZE - 1)
 ELSE
  C = RIGHT(C, SIZE - 1) + LEFT(C, 1)
 END IF

 SLEEP 100, 1
LOOP

FutureBasic

include "NSLog.incl"

_window = 1
_label = 1

void local fn DoIt
  window _window, @"Animation", (0,0,480,270)
  
  subclass textlabel _label, @"Hello World! ", (140,112,210,45)
  ControlSetFont( _label, fn FontLabelFontOfSize( 36 ) )
  ViewSetProperty( _label, @"MoveRight", @(YES) )
  
  timerbegin , 0.1, YES
    CFStringRef string = fn ControlStringValue(_label)
    CFStringRef chr
    BOOL moveRight = fn NumberBoolValue(fn ViewProperty( _label, @"MoveRight" ))
    if ( moveRight )
      chr = right( string, 1 )
      string = fn StringWithFormat( @"%@%@",chr,left(string,len(string)-1) )
    else
      chr = left( string, 1 )
      string = fn StringWithFormat( @"%@%@",right(string,len(string)-1),chr )
    end if
    textlabel _label, string
  timerend
end fn

void local fn DoDialog( ev as long, tag as long )
  select ( ev )
    case _viewMouseDown
      select ( tag )
        case _label
          BOOL moveRight = fn NumberBoolValue(fn ViewProperty( _label, @"MoveRight" ))
          if ( moveRight ) then moveRight == NO else moveRight = YES
          ViewSetProperty( _label, @"MoveRight", @(moveRight) )
      end select
  end select
end fn

fn DoIt

on dialog fn DoDialog

HandleEvents

Gambas

'This code will create the necessary controls on a GUI Form

hLabel As Label                                   'Label needed to display the 'Hello World! " message
hTimer As Timer                                   'Timer to rotate the display
bDirection As Boolean                             'Used to control the direction of the text
'__________________________________________________
Public Sub Form_Open()
Dim hPanel As Panel                               '2 Panels used to centre the Label vertically on the Form
Dim hHBox As HBox                                 'Box to hold the Label

With Me                                           'Set the Form Properties
  .Arrangement = Arrange.Vertical                 'Arrange controls vertically
  .Title = "Animation"                            'Give the Form a Title
  .Height = 75                                    'Set the Height of the Form
  .Width = 225                                    'Set the Width of the Form
End With

hPanel = New Panel(Me)                            'Add a Panel to the Form
HPanel.Expand = True                              'Expand the Panel

hHBox = New HBox(Me)                              'Add a HBox to the Form
hHBox.Height = 28                                 'Set the HBox Height

hLabel = New Label(hHBox) As "LabelAnimation"     'Add new Lable with Event name

With hLabel                                       'Change the hLabel properties
  .Height = 35                                    'Set the Height of the Label
  .Expand = True                                  'Expand the hLabel
  .Font.Bold = True                               'Set the Font to Bold
  .Font.size = 20                                 'Set the Font Size
  .Alignment = Align.Center                       'Centre align the text
  .Tooltip = "Click me to reverse direction"      'Add a Tooltip
  .Border = Border.Plain                          'Add a Border
  .Text = "Hello World! "                         'Add the Text
End With

hPanel = New Panel(Me)                            'Add another Panel
HPanel.Expand = True                              'Expand the Panel

hTimer = New Timer As "Timer1"                    'Add a Timer
hTimer.delay = 500                                'Set the Timer Delay
hTimer.Start                                      'Start the Timer

End
'__________________________________________________
Public Sub LabelAnimation_MouseDown()             'If the hLabel is clicked..

bDirection = Not bDirection                       'Change the value of bDirection

End
'__________________________________________________
Public Sub Timer1_Timer()                         'Timer
Dim sString As String = hLabel.Text               'To hold the text in the Label
Dim sTemp As String                               'Temp string

If bDirection Then                                'If the text is to rotate left then..
  sTemp = Left(sString, 1)                        'Get the first charater of the Label Text e.g 'H'
  sString = Right(sString, -1) & sTemp            'Recreate sString with all the text less the 1st character e.g. 'ello World! ' and add the 1st character to the end e.g. 'ello World! H'
Else                                              'Else if text is to rotate right then..
  sTemp = Right(sString, 1)                       'Get the last charater of the Label Text e.g '!'
  sString = sTemp & Left(sString, -1)             'Recreate sString with all the text less the last character e.g. ' Hello World' and add the last character to the begining e.g. '! Hello World'
End If

hLabel.text = sString                             'Display the result

End

Click here for image of running code

Go

package main

import (
    "log"
    "time"

    "github.com/gdamore/tcell"
)

const (
    msg             = "Hello World! "
    x0, y0          = 8, 3
    shiftsPerSecond = 4
    clicksToExit    = 5
)

func main() {
    s, err := tcell.NewScreen()
    if err != nil {
        log.Fatal(err)
    }
    if err = s.Init(); err != nil {
        log.Fatal(err)
    }
    s.Clear()
    s.EnableMouse()
    tick := time.Tick(time.Second / shiftsPerSecond)
    click := make(chan bool)
    go func() {
        for {
            em, ok := s.PollEvent().(*tcell.EventMouse)
            if !ok || em.Buttons()&0xFF == tcell.ButtonNone {
                continue
            }
            mx, my := em.Position()
            if my == y0 && mx >= x0 && mx < x0+len(msg) {
                click <- true
            }
        }
    }()
    for inc, shift, clicks := 1, 0, 0; ; {
        select {
        case <-tick:
            shift = (shift + inc) % len(msg)
            for i, r := range msg {
                s.SetContent(x0+((shift+i)%len(msg)), y0, r, nil, 0)
            }
            s.Show()
        case <-click:
            clicks++
            if clicks == clicksToExit {
                s.Fini()
                return
            }
            inc = len(msg) - inc
        }
    }
}

Haskell

Using simple graphics
Library: HGL
from HackageDB
import Graphics.HGL.Units (Time, Point, Size, )
import Graphics.HGL.Draw.Monad (Graphic, )
import Graphics.HGL.Draw.Text
import Graphics.HGL.Draw.Font
import Graphics.HGL.Window
import Graphics.HGL.Run
import Graphics.HGL.Utils

import Control.Exception (bracket, )

runAnim = runGraphics $
 bracket
  (openWindowEx "Basic animation task" Nothing (250,50) DoubleBuffered (Just 110))
  closeWindow
  (\w -> do
    f <- createFont (64,28) 0 False False "courier" 
    let loop t dir = do
	  e <- maybeGetWindowEvent w
	  let d = case e of
		  Just (Button _ True False)  -> -dir
		  _ -> dir
	      t' = if d == 1 then last t : init t else tail t ++ [head t]
	  setGraphic w (withFont f $ text (5,10) t') >> getWindowTick w 
	  loop  t' d
	      
    loop "Hello world ! " 1  )

Run within interpreter GHCi:

*Main> runAnim

HicEst

CHARACTER string="Hello World! "

   WINDOW(WINdowhandle=wh, Height=1, X=1, TItle="left/right click to rotate left/right, Y-click-position sets milliseconds period")
   AXIS(WINdowhandle=wh, PoinT=20, X=2048, Y, Title='ms', MiN=0, MaX=400, MouSeY=msec, MouSeCall=Mouse_callback, MouSeButton=button_type)
   direction = 4
   msec = 100    ! initial milliseconds
   DO tic = 1, 1E20
      WRITE(WIN=wh, Align='Center Vertical') string
      IF(direction == 4) string = string(LEN(string)) // string ! rotate left
      IF(direction == 8) string = string(2:) // string(1)       ! rotate right
      WRITE(StatusBar, Name) tic, direction, msec
      SYSTEM(WAIT=msec)
   ENDDO
 END

SUBROUTINE Mouse_callback()
   direction = button_type ! 4 == left button up, 8 == right button up
 END

Icon and Unicon

The following code uses features exclusive to Unicon.

import gui
$include "guih.icn"

class WindowApp : Dialog (label, direction)

  method rotate_left (msg)
    return msg[2:0] || msg[1]
  end

  method rotate_right (msg)
    return msg[-1:0] || msg[1:-1]
  end

  method reverse_direction ()
    direction := 1-direction
  end

  # this method gets called by the ticker, and updates the label
  method tick ()
    static msg := "Hello World! "
    if direction = 0 
      then msg := rotate_left (msg)
      else msg := rotate_right (msg)
    label.set_label(msg)
  end

  method component_setup ()
    direction := 1 # start off rotating to the right
    label := Label("label=Hello World! ", "pos=0,0")
    # respond to a mouse click on the label
    label.connect (self, "reverse_direction", MOUSE_RELEASE_EVENT)
    add (label)

    connect (self, "dispose", CLOSE_BUTTON_EVENT)
    # tick every 100ms
    self.set_ticker (100)
  end
end

# create and show the window
procedure main ()
  w := WindowApp ()
  w.show_modal ()
end

The following code uses features exclusive to Icon.

link graphics

procedure main()
    s := "Hello World! "
    WOpen("size=640,400", "label=Animation")
    Font("typewriter,60,bold")
    direction := 1
    w := TextWidth(s)
    h := WAttrib("fheight")
    x := (WAttrib("width") - w) / 2
    y := (WAttrib("height") - 20 + h) / 2
    
    repeat
    {   if *Pending() > 0 then if (Event() = &lrelease) & (x < &x < x + w) & (y > &y > y-h) then direction := ixor(direction, 1)
        s := s[2 - 3 * direction:0] || s[1:2 - 3 * direction]       
        EraseArea(x, y, w, -h)
        DrawString(x,y - WAttrib("descent")-1,s)
        WFlush()
        delay(250)
    }
end

J

Works with: J8
coinsert'jgl2' [ require'gl2'
 
MESSAGE        =: 'Hello World! '
TIMER_INTERVAL =: 0.5 * 1000                                          NB. Milliseconds
DIRECTION      =: -1                                                  NB. Initial direction is right -->
 
ANIM           =: noun define
  pc anim closeok;pn "Basic Animation in J";
  minwh 350 5;
  cc isi isigraph flush;
  pcenter;pshow;
)
 
anim_run        =: verb def 'wd ANIM,''; ptimer '',":TIMER_INTERVAL'  NB. Set form timer
anim_timer      =: verb def 'glpaint MESSAGE=: DIRECTION |. MESSAGE'  NB. Rotate MESSAGE according to DIRECTION
anim_isi_mbldown=: verb def 'DIRECTION=: - DIRECTION'                 NB. Reverse direction when user clicks
anim_close      =: verb def 'wd ''timer 0; pclose; reset'' '          NB. Shut down timer
anim_isi_paint  =:  verb define
  glclear ''                                                          NB.  Clear out old drawing
  glrgb 255 0 0
  gltextcolor''
  glfont '"courier new" 36'
  gltext MESSAGE
)
 
anim_run ''
Works with: J6
coinsert'jgl2' [ require'gl2'   

MESSAGE          =:  'Hello World! '
TIMER_INTERVAL   =:  0.5 * 1000                                            NB.  Milliseconds
DIRECTION        =:  -1                                                    NB.  Initial direction is right -->

ANIM             =:  noun define
  pc anim nomax nosize;pn "Basic Animation in J";
  xywh 1 1 174 24;cc isi isigraph rightmove bottommove;
  pas 0 0;pcenter;pshow;
)

anim_run         =:  verb def ' wd ANIM,''; timer '',":TIMER_INTERVAL '
sys_timer_z_     =:  verb def ' isiMsg MESSAGE=:  DIRECTION |. MESSAGE '   NB.  Rotate MESSAGE according to DIRECTION
anim_isi_mbldown =:  verb def ' DIRECTION=:  - DIRECTION '                 NB.  Reverse direction when user clicks
anim_close       =:  verb def ' wd ''timer 0; pclose; reset;'' '           NB.  Shut down timer

isiMsg           =:  verb define
  wd'psel anim'
  glclear ''                                                               NB.  Clear out old drawing
  glfont '"courier new" 36'
  gltext y
  glpaint ''                                                               NB.  Copy to screen
)

anim_run ''

Java

Library: Swing
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.WindowConstants;

public class Rotate {

    private static class State {
        private final String text = "Hello World! ";
        private int startIndex = 0;
        private boolean rotateRight = true;
    }

    public static void main(String[] args) {
        State state = new State();

        JLabel label = new JLabel(state.text);
        label.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent event) {
                state.rotateRight = !state.rotateRight;
            }
        });

        TimerTask task = new TimerTask() {
            public void run() {
                int delta = state.rotateRight ? 1 : -1;
                state.startIndex = (state.startIndex + state.text.length() + delta) % state.text.length();
                label.setText(rotate(state.text, state.startIndex));
            }
        };
        Timer timer = new Timer(false);
        timer.schedule(task, 0, 500);

        JFrame rot = new JFrame();
        rot.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
        rot.add(label);
        rot.pack();
        rot.setLocationRelativeTo(null);
        rot.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosed(WindowEvent e) {
                timer.cancel();
            }
        });
        rot.setVisible(true);
    }

    private static String rotate(String text, int startIdx) {
        char[] rotated = new char[text.length()];
        for (int i = 0; i < text.length(); i++) {
            rotated[i] = text.charAt((i + startIdx) % text.length());
        }
        return String.valueOf(rotated);
    }
}

JavaScript + HTML

<html> <head>
    <title>RC: Basic Animation</title>
    <script type="text/javascript">
        function animate(id) {
            var element = document.getElementById(id);
            var textNode = element.childNodes[0]; // assuming no other children

            var text = textNode.data;
            var reverse = false;
            
            element.onclick = function () { reverse = !reverse; };
            
            setInterval(function () {
                if (reverse)
                    text = text.substring(1) + text[0];
                else
                    text = text[text.length - 1] + text.substring(0, text.length - 1);
                textNode.data = text;
            }, 100);
        }
    </script>
</head> <body onload="animate('target')">
    <pre id="target">Hello World! </pre>
</body> </html>

JavaScript + SVG

<svg xmlns="http://www.w3.org/2000/svg"
     width="100" height="40">
    <script type="text/javascript">
        function animate(element) {
            var textNode = element.childNodes[0]; // assuming no other children
            var text = textNode.data;
            var reverse = false;

            element.onclick = function () { reverse = !reverse; };
            
            setInterval(function () {
                if (reverse)
                    text = text.substring(1) + text[0];
                else
                    text = text[text.length - 1] + text.substring(0, text.length - 1);
                textNode.data = text;
            }, 100);
        }
    </script>
  
    <rect width="100" height="40" fill="yellow"/>
    <text x="2" y="20" onload="animate(this);">Hello World! </text>
</svg>

Julia

Library: Julia/Tk
using Tk

const frameinterval = 0.12 # partial seconds between change on screen display

function windowanim(stepinterval::Float64)
    wind = Window("Animation", 300, 100)
    frm = Frame(wind)
    hello = "Hello World!                                           "
    but = Button(frm, width=30, text=hello)
    rightward = true
    callback(s) = (rightward = !rightward)
    bind(but, "command", callback)
    pack(frm, expand=true, fill = "both")
    pack(but, expand=true, fill = "both")
    permut = [hello[i:end] * hello[1:i-1] for i in length(hello)+1:-1:2]
    ppos = 1
    pmod = length(permut)
    while true
        but[:text] = permut[ppos]
        sleep(stepinterval)
        if rightward
            ppos += 1
            if ppos > pmod
                ppos = 1
            end
        else
            ppos -= 1
            if ppos < 1
                ppos = pmod
            end
        end
    end
end

windowanim(frameinterval)
Library: Julia/Gtk
using Gtk.ShortNames
 
const frameinterval = 0.12 # partial seconds between change on screen display
 
function textanimation(stepinterval::Float64)
    hello = "Hello World!                        "
    win = Window("Animation", 210, 40) |> (Frame() |> (but = Button("Switch Directions")))
    rightward = true
    switchdirections(s) = (rightward = !rightward)
    signal_connect(switchdirections, but, "clicked")
    permut = [hello[i:end] * hello[1:i-1] for i in length(hello)+1:-1:2]
    ppos = 1
    pmod = length(permut)
    nobreak = true
    endit(w) = (nobreak = false)    
    signal_connect(endit, win, :destroy)
    showall(win)
    while nobreak
        setproperty!(but, :label, permut[ppos])        
        sleep(stepinterval)
        if rightward
            ppos += 1
            if(ppos > pmod)
                ppos = 1
            end
        else
            ppos -= 1
            if(ppos < 1)
                ppos = pmod
            end
        end
    end
end

textanimation(frameinterval)

Kotlin

Translation of: Java
// version 1.1.0

import java.awt.Dimension
import java.awt.event.MouseAdapter
import java.awt.event.MouseEvent
import java.util.*
import javax.swing.JFrame
import javax.swing.JLabel

class Rotate : JFrame() {
    val text = "Hello World! "
    val label = JLabel(text)
    var rotRight = true
    var startIdx = 0

    init {
        preferredSize = Dimension(96, 64)
        label.addMouseListener(object: MouseAdapter() {
            override fun mouseClicked(evt: MouseEvent) {
                rotRight = !rotRight
            }
        })
        add(label)
        pack()
        defaultCloseOperation = JFrame.EXIT_ON_CLOSE
        isVisible = true
    }
}

fun getRotatedText(text: String, startIdx: Int): String {
    val ret = StringBuilder()
    var i = startIdx
    do {
        ret.append(text[i++])
        i %= text.length
    }
    while (i != startIdx)
    return ret.toString()
}

fun main(args: Array<String>) {
    val rot = Rotate()
    val task = object : TimerTask() {
        override fun run() {
            if (rot.rotRight) {
                if (--rot.startIdx < 0) rot.startIdx += rot.text.length
            }
            else {
                if (++rot.startIdx >= rot.text.length) rot.startIdx -= rot.text.length
            }
            rot.label.text = getRotatedText(rot.text, rot.startIdx)
        }
    }
    Timer(false).schedule(task, 0, 500)
}

LabVIEW

This image is a VI Snippet, an executable image of LabVIEW code. The LabVIEW version is shown on the top-right hand corner. You can download it, then drag-and-drop it onto the LabVIEW block diagram from a file browser, and it will appear as runnable, editable code.

Liberty BASIC

    txt$ = "Hello World! "
    txtLength = len(txt$)
    direction=1

    NoMainWin

    open "Rosetta Task: Animation" for graphics_nsb as #demo
    #demo "Trapclose [quit]"
    #demo "down"
    #demo "Font Verdana 20 Bold"
    #demo "When leftButtonUp [changedirection]"

    timer 150 , [draw]
    wait

[draw]
    if direction then
        txt$ = right$(txt$, 1);left$(txt$, txtLength - 1)
    else
        txt$ = right$(txt$, txtLength - 1);left$(txt$, 1)
    end if
    #demo "discard"
    #demo "place 50 100"
    #demo "\";txt$
    wait

[changedirection]
    direction = not(direction)
    wait

[quit]
    timer 0
    close #demo
    end

Works with: UCB Logo
to rotate.left :thing
  output lput first :thing butfirst :thing
end
to rotate.right :thing
  output fput last :thing butlast :thing
end

make "text "|Hello World! |
make "right? "true

to step.animation
  label :text			; graphical
  ; type char 13  type :text	; textual
  wait 6			; 1/10 second
  if button <> 0 [make "right? not :right?]
  make "text ifelse :right? [rotate.right :text] [rotate.left :text]
end

hideturtle
until [key?] [step.animation]


Lua

Works with: LÖVE
function love.load()
	text = "Hello World! "
	length = string.len(text)
	
	
	update_time = 0.3
	timer = 0
	right_direction = true
	
	
	local width, height = love.graphics.getDimensions( )

	local size = 100
	local font = love.graphics.setNewFont( size )
	local twidth = font:getWidth( text )
	local theight = font:getHeight( )
	x = width/2 - twidth/2
	y = height/2-theight/2
	
end

 
function love.update(dt)
	timer = timer + dt
	if timer > update_time then
		timer = timer - update_time
		if right_direction then
			text = string.sub(text, 2, length) .. string.sub(text, 1, 1)
		else
			text = string.sub(text, length, length) .. string.sub(text, 1, length-1)
		end
	end
end


function love.draw()
	love.graphics.print (text, x, y)
end

function love.keypressed(key, scancode, isrepeat)
	if false then
	elseif key == "escape" then
		love.event.quit()
	end
end

function love.mousepressed( x, y, button, istouch, presses )
	right_direction = not right_direction
end

M2000 Interpreter

Using Blink Event

Any Button can use a blink, a timer which return a changed value, true or false, but here we didn't use it (so we didn't Read from event's stack of values. We can place Stack statement to view this stack in console).

Module UseBlink { 
      Def boolean direction=True
      rotating$ =lambda$ a$="Hello World! " (direction as boolean)->{
            =a$
            a$=if$(direction->right$(a$,1)+mid$(a$,1, len(a$)-1), mid$(a$,2)+left$(a$,1))
      }
      Declare MyForm Form
      Declare MyButton Button  Form MyForm
      With MyButton, "Caption" as MyButtonCaption$, "Blink", 200
      Method MyForm,"Move", 1000,1000,6000,4000
      Method MyButton,"Move", 1000,1700,4000,600
      Function MyButton.Blink {
            Rem Stack : Refresh  ' to refresh the console window
            MyButtonCaption$=rotating$(direction)
      }
      Function MyButton.Click {
            direction~
      }
      Function MyForm.Click {
            direction~
      }
      With MyForm, "Title", "Animation"
      Method MyForm, "Show", 1
      Threads Erase
}
UseBlink

Using Every Statement

We can use console as window too. Although we don't have events for this window, we can read mouse button. Also we use split screen so first line (line 0) has the title, and all other lines are cleared before we print on it, every 200ms

Module UseEvery {
            Window 16, 6000,4000;
            Print "Animation"
            Def boolean direction=True
            rotating$ =lambda$ a$="Hello World! " (direction as boolean)->{
                  =a$
                  a$=if$(direction->right$(a$,1)+mid$(a$,1, len(a$)-1), mid$(a$,2)+left$(a$,1))
            }
            Every 200 {
                  Cls #225577,1
                  Print @(2,height/2),rotating$(direction)
                  if mouse=1 then direction~
                  if mouse=2 then exit
            }
}
UseEvery

Using Thread

We use form layer as console (we can't use input statements for those layers). We setup a thread to change form layer, at an interval of 200ms.

When a thread created saves the current layer, to use it in each iteration.

Module UseThread { 
      Def boolean direction=True
      rotating$ =lambda$ a$="Hello World! " (direction as boolean)->{
            =a$
            a$=if$(direction->right$(a$,1)+mid$(a$,1, len(a$)-1), mid$(a$,2)+left$(a$,1))
      }
      Declare MyForm Form
      Layer MyForm {
            Thread {
                  Cls #225577,0
                  Print @(2,height/2),rotating$(direction)
            } as M interval 200
      }
      Function MyForm.Click {
            direction~
      }
      
      With MyForm, "Title", "Animation"
      
      Method MyForm, "Show", 1
      Threads Erase
}
UseThread

Maple

First, create a textbox, and by right-clicking it, and selecting component properties, change its name to "Text". Then, create 2 buttons, changing the caption on one to "Reverse", and the other to "Forward". In the edit click action of each respective button, you will put:

ScrollText(GP("Text",value),"Forward",65);

and:

ScrollText(GP("Text",value),"Reverse",65);

Then in the startup actions, accessed by clicking the 2 gears, add this:

macro(SP=DocumentTools:-SetProperty, GP=DocumentTools:-GetProperty);
SP("Text",value,"Hello World! "); 
ScrollText := proc( msg, direction::identical("Forward","Reverse"),n::posint:=20 )
    local word, count;
    word:=msg; 
    count:=0;

    while count<n do
        if direction = "Reverse" then
            word:=cat(word[2..-1],word[1]):
        else
            word:=cat(word[-1],word[1..-2]):
        end if;
        SP("Text",value,word,refresh); 
        Threads:-Sleep(0.1);
                   count:=count+1:
    end do:
end proc:

Mathematica / Wolfram Language

mystring = "Hello World! ";
Scroll[str_, dir_] := StringJoin @@ RotateLeft[str // Characters, dir];
GiveString[dir_] := (mystring = Scroll[mystring, dir]);
CreateDialog[{
   DynamicModule[{direction = -1}, 
    EventHandler[
     Dynamic[TextCell[
       Refresh[GiveString[direction], UpdateInterval -> 1/8]], 
      TrackedSymbols -> {}], {"MouseClicked" :> (direction *= -1)}]]
   }];

MAXScript

Creates a window with a rotating label and 2 buttons to change the direction.

try destroydialog animGUI catch ()

global userDirection = "right"

fn reverseStr str: direction: =
(
	local lastChar = str[str.count]
	local firstChar = str[1]
	local newStr = ""
	local dir 
	
	if direction == unsupplied then dir = "left" else dir = direction
		
	case dir of
	(
		"right": (newstr = lastChar + (substring str 1 (str.count-1)))
		"left": (newstr = (substring str 2 (str.count))+firstChar)
	)
	
	return newstr
)

rollout animGUI "Hello World" width:200
(
	button switchToLeft "<--" pos:[40,0] height:15
	label HelloWorldLabel "Hello World! " pos:[80,0]
	button switchToRight "-->" pos:[150,0] height:15
	
	timer activeTimer interval:70 active:true
	
	on activeTimer tick do
	(
		HelloWorldLabel.text = reverseStr str:(HelloWorldLabel.text) direction:userDirection
	)
	
	on switchToLeft pressed do
	(
		userDirection = "left"
	)
	
	on switchToRight pressed do
	(
		userDirection = "right"
	)
)

createdialog animGUI

MiniScript

Works with: Mini Micro
clear
s = "Hello World! "
toggle = true
while true
	text.row = 12
	text.column = 15
	print " " + s + " "
	wait 0.1
	if key.available then
		toggle = not toggle
		key.clear
		yield
	end if
	if toggle then
		s = s[-1] + s[:-1]
	else
		s = s[1:] + s[0]
	end if
end while

Alternate version that scrolls pixel by pixel rather than character by character.

clear
txt = "Hello World! "
width = txt.len * 20 + 4
gfx.print txt,0,608,color.white, "large"
toggle = false

while true
	img1 = gfx.getImage(toggle, 608, width - 1, 32)
	img2 = gfx.getImage((not toggle) * (width - 1), 608, 1, 32)
	gfx.drawImage img1, (not toggle), 608
	gfx.drawImage img2, toggle * (width - 1), 608
	if key.available then
		toggle = not toggle
		key.clear
	end if
	yield
end while

Nim

Library: gintro
import gintro/[glib, gobject, gdk, gtk, gio]

type

  # Scrolling direction.
  ScrollDirection = enum toLeft, toRight

  # Data transmitted to update callback.
  UpdateData = ref object
    label: Label
    scrollDir: ScrollDirection

#---------------------------------------------------------------------------------------------------

proc update(data: UpdateData): gboolean =
  ## Update the text, scrolling to the right or to the left according to "data.scrollDir".

  data.label.setText(if data.scrollDir == toRight: data.label.text[^1] & data.label.text[0..^2]
                     else: data.label.text[1..^1] & data.label.text[0])
  result = gboolean(1)

#---------------------------------------------------------------------------------------------------

proc changeScrollingDir(evtBox: EventBox; event: EventButton; data: UpdateData): bool =
  ## Change scrolling direction.

  data.scrollDir = ScrollDirection(1 - ord(data.scrollDir))

#---------------------------------------------------------------------------------------------------

proc activate(app: Application) =
  ## Activate the application.

  let window = app.newApplicationWindow()
  window.setSizeRequest(150, 50)
  window.setTitle("Animation")

  # Create an event box to catch the button press event.
  let evtBox = newEventBox()
  window.add(evtBox)

  # Create the label and add it to the event box.
  let label = newLabel("Hello World! ")
  evtBox.add(label)

  # Create the update data.
  let data = UpdateData(label: label, scrollDir: toRight)

  # Connect the "button-press-event" to the callback to change scrolling direction.
  discard evtBox.connect("button-press-event", changeScrollingDir, data)

  # Create a timer to update the label and simulate scrolling.
  timeoutAdd(200, update, data)

  window.showAll()

#———————————————————————————————————————————————————————————————————————————————————————————————————

let app = newApplication(Application, "Rosetta.animation")
discard app.connect("activate", activate)
discard app.run()

Oz

functor
import
   Application
   QTk at 'x-oz://system/wp/QTk.ozf'
define
   proc {Start}
      Label
      Window = {CreateWindow ?Label}
      Animation = {New LabelAnimation init(Label delay:125)}
   in
      {Window show}
      {Animation go}
   end
   
   fun {CreateWindow ?Label}
      Courier = {QTk.newFont font(family:courier size:14)}
      GUI = td(
               title:"Basic Animation"
               label(text:"Hello World! " handle:Label font:Courier)
               action:proc {$} {Application.exit 0} end
               )
   in
      {QTk.build GUI}
   end

   class LabelAnimation from Time.repeat
      attr
         activeShifter:ShiftRight
         otherShifter:ShiftLeft
      feat
         label
         
      meth init(Label delay:Delay<=100)
         self.label = Label
         {self setRepAll(action:Animate delay:Delay)}
         {Label bind(event:"<Button-1>" action:self#Revert)}
      end

      meth Animate
         OldText = {self.label get(text:$)}
         NewText = {@activeShifter OldText}
      in
         {self.label set(text:NewText)}
      end

      meth Revert
         otherShifter := (activeShifter := @otherShifter)
      end
   end

   fun {ShiftRight Xs}
      {List.last Xs}|{List.take Xs {Length Xs}-1}
   end
   
   fun {ShiftLeft Xs}
      {Append Xs.2 [Xs.1]}
   end
   
   {Start}
end

Pascal

LCL (Lazarus)

program HelloWorldAnimatedGUI;

uses {$IFDEF UNIX} {$IFDEF UseCThreads}
  cthreads, {$ENDIF} {$ENDIF}
  Interfaces, // this includes the LCL widgetset
  Forms,
  Classes,
  Controls,
  StdCtrls,
  ExtCtrls;

type
  { TFrmHelloWorldAnim }
  TFrmHelloWorldAnim = class(TForm)
    constructor CreateNew(AOwner: TComponent; Num: integer = 0); override;
    procedure lblTextAnimateClick(Sender: TObject);
    procedure tmrAnimateTimer(Sender: TObject);
  private
    { private declarations }
    lblTextAnimate: TLabel;
    tmrAnimate: TTimer;
    FDirection: boolean;
  public
    { public declarations }
  end;

var
  FrmHelloWorldAnim: TFrmHelloWorldAnim;

  { TFrmHelloWorldAnim }

  constructor TFrmHelloWorldAnim.CreateNew(AOwner: TComponent; Num: integer);
  begin
    inherited CreateNew(AOwner, Num);
    Height := 50;
    lblTextAnimate := TLabel.Create(self);
    with lblTextAnimate do
    begin
      Caption := 'Hello World! ';
      Align := alClient;
      Alignment := taCenter;
      font.Name := 'Courier New';
      font.size := 20;
      OnClick := @lblTextAnimateClick;
      Parent := self;
    end;
    tmrAnimate := TTimer.Create(self);
    with tmrAnimate do
    begin
      Interval := 100;
      OnTimer := @tmrAnimateTimer;
    end;
  end;

  procedure TFrmHelloWorldAnim.lblTextAnimateClick(Sender: TObject);
  begin
    FDirection := not FDirection;
  end;

  procedure TFrmHelloWorldAnim.tmrAnimateTimer(Sender: TObject);
  begin
    if FDirection then
      lblTextAnimate.Caption :=
        copy(lblTextAnimate.Caption, length(lblTextAnimate.Caption), 1) +
        copy(lblTextAnimate.Caption, 1, length(lblTextAnimate.Caption) - 1)
    else
      lblTextAnimate.Caption :=
        copy(lblTextAnimate.Caption, 2, length(lblTextAnimate.Caption) - 1) +
        copy(lblTextAnimate.Caption, 1, 1);
  end;

begin
  RequireDerivedFormResource := False;
  Application.Initialize;
  Application.CreateForm(TFrmHelloWorldAnim, FrmHelloWorldAnim);
  Application.Run;
end.

Example can be compiled by FreePascal/Lazarus. Did all in one file, normally you have the project-file, the form-unit- and the form-resource-file.

Perl

use Tk;
use Time::HiRes qw(sleep);

my $msg    = 'Hello World! ';
my $first  = '.+';
my $second = '.';

my $mw = Tk::MainWindow->new(-title => 'Animated side-scroller',-bg=>"white");
$mw->geometry ("400x150+0+0");

$mw->optionAdd('*Label.font', 'Courier 24 bold' );

my $scroller = $mw->Label(-text => "$msg")->grid(-row=>0,-column=>0);
$mw->bind('all'=> '<Key-Escape>' => sub {exit;});
$mw->bind("<Button>" => sub { ($second,$first) = ($first,$second) });

$scroller->after(1, \&display );
MainLoop;

sub display {
    while () {
        sleep 0.25;
        $msg =~ s/($first)($second)/$2$1/;
        $scroller->configure(-text=>"$msg");
        $mw->update();
    }
}

Phix

Library: Phix/pGUI

Clicking on the label changes the animation direction.

-- demo\rosetta\Animation.exw
with javascript_semantics
include pGUI.e

string hw = "Hello World!     "
bool direction = true
Ihandle label

function button_cb(Ihandle /*ih*/, integer /*button*/, pressed, /*x*/, /*y*/, atom /*pStatus*/)
    if pressed then
        direction = not direction
    end if
    return IUP_CONTINUE
end function

function timer_cb(Ihandle /*ih*/)
    hw = iff(direction ? hw[$] & hw[1..-2]
                       : hw[2..$] & hw[1])
    IupSetAttribute(label,"TITLE",hw)
    return IUP_IGNORE
end function

procedure main()
    IupOpen()
    label = IupLabel(hw,`FONT="Verdana, 18"`)
    IupSetCallback(label, "BUTTON_CB", Icallback("button_cb"))
    Ihandle dlg = IupDialog(label,"TITLE=Animation, DIALOGFRAME=YES, CHILDOFFSET=70x40, SIZE=200x80")
    IupShow(dlg)
    Ihandle hTimer = IupTimer(Icallback("timer_cb"), 160)
    if platform()!=JS then
        IupMainLoop()
        IupClose()
    end if
end procedure

main()

PicoLisp

Plain text

A plain text version. The following script works in an XTerm window.

#!/usr/bin/picolisp /usr/lib/picolisp/lib.l

(prin "^[[?9h")  # Mouse reporting on

(setq Dir 1  Text (chop "Hello World! "))

(loop
   (prin (do Dir (rot Text)))
   (when (= "^[" (key 200))
      (key) (key)
      (when (= " " (key))  # Left button
         (setq Dir (if (= 1 Dir) 12 1)) )
      (key) (key) )
   (do (length Text) (prin "^H")) )

HTML/JavaScript

The standard PicoLisp GUI is HTTP based. Connect your browser to http://localhost:8080 after starting the following script.

The scrolling text is displayed in a button. Clicking on the button reverses the scroll direction.

#!/usr/bin/picolisp /usr/lib/picolisp/lib.l

(load "@ext.l" "@lib/http.l" "@lib/xhtml.l" "@lib/form.l")

(one *Dir)

(de start ()
   (app)
   (action
      (html 0 "Animation" "@lib.css" NIL
         (form NIL
            (gui '(+Button)
               '(pack (do *Dir (rot '`(chop "Hello World! "))))
               '(setq *Dir (if (= 1 *Dir) 12 1)) )
            (gui '(+Click +Auto +Button) 400 'This 1000 "Start") ) ) ) )

(server 8080 "!start")
(wait)

Java/Swing

This solution works on ErsatzLisp, the Java version of PicoLisp.

#!ersatz/pil

(setq
   Dir 1
   Text (chop "Hello World! ")
   Frame (java "javax.swing.JFrame" T "Animation")
   Label (java "javax.swing.JLabel" T (pack Text)) )

(java Label 'addMouseListener
   (interface "java.awt.event.MouseListener"
      'mouseClicked '((Ev) (setq Dir (if (= 1 Dir) 12 1)))
      'mouseEntered nil
      'mouseExited nil
      'mousePressed nil
      'mouseReleased nil ) )

(java Frame 'add Label)
(java Frame 'pack)
(java Frame 'setVisible T)
(loop
   (wait 200)
   (java Label 'setText (pack (do Dir (rot Text)))) )

Processing

String txt = "Hello, world! ";
boolean dir = true;

void draw(){
  background(128);
  text(txt, 10, height/2);
  if(frameCount%10==0){
    if(dir) {
      txt = rotate(txt, 1);
    } else {
      txt = rotate(txt, txt.length()-1);
    }
  }
}

void mouseReleased(){
  dir = !dir;
}

String rotate(String text, int startIdx) {
  char[] rotated = new char[text.length()];
  for (int i = 0; i < text.length(); i++) {
    rotated[i] = text.charAt((i + startIdx) % text.length());
  }
  return String.valueOf(rotated);
}

Processing Python mode

When the user clicks on the (windowed) text, it should reverse its direction.

txt = "Hello, world! "
left = True

def draw():
    global txt
    background(128)
    text(txt, 10, height / 2)
    if frameCount % 10 == 0:
        if (left):
            txt = rotate(txt, 1)
        else:
            txt = rotate(txt, -1)
        println(txt)

def mouseReleased():
    global left
    left = not left

def rotate(text, startIdx):
    rotated = text[startIdx:] + text[:startIdx]
    return rotated

Prolog

SWI-Prolog has a grapghic interface XPCE.

:- use_module(library(pce)).

animation :-
    new(D, window('Animation')),
    new(Label, label(hello, 'Hello world ! ')),
    send(D, display, Label, point(1,10)),
    new(@animation, animation(Label)),
    send(D, recogniser,
         new(_G, my_click_gesture(left, ''))),

    send(D, done_message, and(message(@animation, free),
                  message(@receiver, destroy))),
    send(D, open),
    send(@animation?mytimer, start).


:- pce_begin_class(animation(label), object).
variable(label, object,  both, "Display window").
variable(delta,    object, both,  "increment of the angle").
variable(mytimer, timer, both, "timer of the animation").

initialise(P, W:object) :->
        "Creation of the object"::
        send(P, label, W),
        send(P, delta, to_left),
    send(P, mytimer, new(_, timer(0.5,message(P, anim_message)))).

% method called when the object is destroyed
% first the timer is stopped
% then all the resources are freed
unlink(P) :->
    send(P?mytimer, stop),
    send(P, send_super, unlink).


% message processed by the timer
anim_message(P) :->
    get(P, label, L),
    get(L, selection, S),
    get(P, delta, Delta),
    compute(Delta, S, S1),
    new(A, name(S1)),
    send(L, selection, A).


:- pce_end_class.

:- pce_begin_class(my_click_gesture, click_gesture,
           "Click in a window").

class_variable(button, button_name, left,
           "By default click with left button").

terminate(G, Ev:event) :->
    send(G, send_super, terminate, Ev),
    get(@animation, delta, D),
    (   D = to_left -> D1 = to_right; D1 = to_left),
    send(@animation, delta, D1).

:- pce_end_class.


% compute next text to be dispalyed
compute(to_right, S, S1) :-
    get(S, size, Len),
    Len1 is Len - 1,
    get(S, sub, Len1, Str),
    get(S, delete_suffix, Str, V),
    get(Str, append, V, S1).

compute(to_left, S, S1) :-
    get(S, sub, 0, 1, Str),
    get(S, delete_prefix, Str, V),
    get(V, append, Str, S1).

PureBasic

OpenWindow(0,0,0,500,100,"Hello World!",#PB_Window_ScreenCentered|#PB_Window_SystemMenu)

text$ = "Hello World! "
direction = 1

LoadFont(0,"",60)
ButtonGadget(0,2,2,496,96,text$) : SetGadgetFont(0,FontID(0))

Repeat
  event = WaitWindowEvent(50)
  Select event
    Case #PB_Event_Gadget
      If EventGadget() = 0
        direction*-1
      EndIf
    Case #PB_Event_CloseWindow
      End
  EndSelect
  
  If ElapsedMilliseconds()-tick > 400
    offset+direction
    If offset > Len(text$)-1
      offset = 0
    ElseIf offset < 0
      offset = Len(text$)-1
    EndIf
    SetGadgetText(0,Mid(text$,offset+1)+Left(text$,offset))
    tick = ElapsedMilliseconds()
  EndIf
ForEver

Python

Using PyQt

Library: PyQt5
#!/usr/bin/env python3
import sys

from PyQt5.QtCore import QBasicTimer, Qt
from PyQt5.QtGui import QFont
from PyQt5.QtWidgets import QApplication, QLabel


class Marquee(QLabel):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.right_to_left_direction = True
        self.initUI()
        self.timer = QBasicTimer()
        self.timer.start(80, self)

    def initUI(self):
        self.setWindowFlags(Qt.FramelessWindowHint)
        self.setAttribute(Qt.WA_TranslucentBackground)
        self.setText("Hello World! ")
        self.setFont(QFont(None, 50, QFont.Bold))
        # make more irritating for the authenticity with <marquee> element
        self.setStyleSheet("QLabel {color: cyan; }")

    def timerEvent(self, event):
        i = 1 if self.right_to_left_direction else -1
        self.setText(self.text()[i:] + self.text()[:i])  # rotate

    def mouseReleaseEvent(self, event):  # change direction on mouse release
        self.right_to_left_direction = not self.right_to_left_direction

    def keyPressEvent(self, event):  # exit on Esc
        if event.key() == Qt.Key_Escape:
            self.close()


app = QApplication(sys.argv)
w = Marquee()
# center widget on the screen
w.adjustSize()  # update w.rect() now
w.move(QApplication.instance().desktop().screen().rect().center()
       - w.rect().center())
w.show()
sys.exit(app.exec())

Using pygame

Library: pygame
import pygame, sys
from pygame.locals import *
pygame.init()

YSIZE = 40
XSIZE = 150

TEXT = "Hello World! "
FONTSIZE = 32

LEFT = False
RIGHT = True

DIR = RIGHT

TIMETICK = 180
TICK = USEREVENT + 2

TEXTBOX = pygame.Rect(10,10,XSIZE,YSIZE)

pygame.time.set_timer(TICK, TIMETICK)

window = pygame.display.set_mode((XSIZE, YSIZE))
pygame.display.set_caption("Animation")

font = pygame.font.SysFont(None, FONTSIZE)
screen = pygame.display.get_surface()

def rotate():
    index = DIR and -1 or 1
    global TEXT
    TEXT = TEXT[index:]+TEXT[:index]

def click(position):
    if TEXTBOX.collidepoint(position):
        global DIR
        DIR = not DIR

def draw():
    surface = font.render(TEXT, True, (255,255,255), (0,0,0))
    global TEXTBOX
    TEXTBOX = screen.blit(surface, TEXTBOX)
    
def input(event):
    if event.type == QUIT:
        sys.exit(0)
    elif event.type == MOUSEBUTTONDOWN:
        click(event.pos)
    elif event.type == TICK:
        draw()
        rotate()

while True:
    input(pygame.event.wait())
    pygame.display.flip()

Using Tkinter

import Tkinter as tki

def scroll_text(s, how_many):
    return s[how_many:] + s[:how_many]

direction = 1
tk = tki.Tk()
var = tki.Variable(tk)

def mouse_handler(point):
    global direction
    direction *= -1

def timer_handler():
    var.set(scroll_text(var.get(),direction))
    tk.after(125, timer_handler)

var.set('Hello, World! ')
tki.Label(tk, textvariable=var).pack()
tk.bind("<Button-1>", mouse_handler)
tk.after(125, timer_handler)
tk.title('Python Animation')
tki.mainloop()

Quick BASIC

Works with: QB 4.5
Works with: QB 7.1

Before, load the QB.EXE with /L QB.QLB key, or QBX.EXE with /L QBX.QLB key, or just /L key any of them. That's the easiest way to get the mouse in DOS. Pressing to change direction should be on the text. To exit push the right button anywhere.

'here accordingly to the version, QB or QBX
REM $INCLUDE: 'QBX.BI'

DIM REGS AS REGTYPE
DIM C AS STRING, SIZ AS INTEGER
DIM I AS DOUBLE, DIRE AS INTEGER
C = "Hello World! "
SIZ = LEN(C)

SCREEN 12
'turn the cursor visible
REGS.AX = 1
INTERRUPT 51, REGS, REGS

DO
 I = TIMER
 LOCATE 1, 1
 PRINT C
 
 REGS.AX = 5            'read mouse's queue of occurred pressings
 REGS.BX = 0            'the left button
 INTERRUPT 51, REGS, REGS
 
 'BX is the selected button's quantity of occurred pressings
 IF REGS.BX <> 0 THEN
  IF REGS.CX >= 0 AND REGS.CX < SIZ * 8 AND REGS.DX >= 0 AND REGS.DX < 16 THEN
   DIRE = 1 - DIRE
  END IF
 END IF
 
 'AX is all buttons' status
 IF (REGS.AX AND 2) <> 0 THEN EXIT DO
 
 IF DIRE = 0 THEN
  C = RIGHT$(C, 1) + LEFT$(C, SIZ - 1)
 ELSE
  C = RIGHT$(C, SIZ - 1) + LEFT$(C, 1)
 END IF
 
 DO WHILE TIMER < I + .08
  IF TIMER < I THEN I = I - 86400       'midnight checking
 LOOP
LOOP

R

Library: gWidgets

The basic principle is:create a window with a label in it, then add a handler to the label for rotating the string, and another for changing direction on a click. Use of the tag function allows you to store the text flow direction as an attribute of the label.

rotate_string <- function(x, forwards) 
{ 
   nchx <- nchar(x) 
   if(forwards) 
   {                                   
      paste(substr(x, nchx, nchx), substr(x, 1, nchx - 1), sep = "") 
   } else 
   {                                                           
      paste(substr(x, 2, nchx), substr(x, 1, 1), sep = "") 
   } 
}

handle_rotate_label <- function(obj, interval = 100)
{
  addHandlerIdle(obj,
    handler = function(h, ...)
    {
       svalue(obj) <- rotate_string(svalue(obj), tag(obj, "forwards"))
    },
    interval = interval
  )
}

handle_change_direction_on_click <- function(obj)
{
  addHandlerClicked(obj,
    handler = function(h, ...)
    {
      tag(h$obj, "forwards") <- !tag(h$obj, "forwards")
    }
  )
}

library(gWidgets)
library(gWidgetstcltk) #or library(gWidgetsRGtk2) or library(gWidgetsrJava)              
lab <- glabel("Hello World! ", container = gwindow())  
tag(lab, "forwards") <- TRUE 
handle_rotate_label(lab) 
handle_change_direction_on_click(lab)

Racket

#lang racket/gui

;; One of 'left or 'right
(define direction 'left)

;; Set up the GUI
(define animation-frame%
  (class frame%
    (super-new [label "Animation"])
    ;; reverse direction on a click
    (define/override (on-subwindow-event win evt)                    
      (when (send evt button-down?)
        (set! direction
              (if (eq? direction 'left)
                  'right
                  'left))))))

(define frame (new animation-frame%))
(define msg (new message%
                 [label "Hello World! "]
                 [parent frame]))

;; Timer callback to adjust the message
(define (callback)
  (define old-label (send msg get-label))
  (define len (string-length old-label))
  (if (eq? direction 'left)
      (send msg set-label
            (string-append (substring old-label 1)
                           (substring old-label 0 1)))
      (send msg set-label
            (string-append (substring old-label (- len 1) len)
                           (substring old-label 0 (- len 1))))))

;; Set a timer and go
(define timer (new timer%
                   [notify-callback callback]
                   [interval 500]))

(send frame show #t)


This is a more functional approach - there is no explicit mutation, everything is handled through event callbacks.

#lang racket
(require racket/match)
(require 2htdp/image)
(require 2htdp/universe)

; program state
; value -> no. of rotations of string
; step -> number (positive for right rotations, else negative)
(struct rotations (value step) #:transparent)

(define STR "Hello World! ")
(define LEN (string-length STR))

; font
(define COLOR "red")
(define TEXT-SIZE 16)   ;; pixels

(define (render rots)
  (define val (rotations-value rots))
  (text (string-append (substring STR val)
                       (substring STR 0 val))
        TEXT-SIZE
        COLOR))

(define (update rots)
  (match rots
      [(rotations val dir)
       (rotations (modulo (+ val dir) LEN) dir)]))

; reverse direction on mouse click
(define (handle-mouse rots x y event)
  (case event
    ; "button-up" indicates mouse click
    [("button-up") (rotations (rotations-value rots)
                              (- (rotations-step rots)))]
    [else rots]))

; start GUI, with given event handlers
(big-bang (rotations 0 1)
  [to-draw render]
  [on-tick update]
  [on-mouse handle-mouse])

Raku

(formerly Perl 6)

Works with: Rakudo version 2019.03
use GTK::Simple;
use GTK::Simple::App;

my $app    = GTK::Simple::App.new(:title<Animation>);
my $button = GTK::Simple::Button.new(label => 'Hello World! ');
my $vbox   = GTK::Simple::VBox.new($button);

my $repeat = $app.g-timeout(100); # milliseconds

my $dir = 1;
$button.clicked.tap({ $dir *= -1 });

$repeat.tap( &{$button.label = $button.label.comb.List.rotate($dir).join} );

$app.set-content($vbox);

$app.run;

REBOL

REBOL [
	Title: "Basic Animation"
	URL: http://rosettacode.org/wiki/Basic_Animation
]

message: "Hello World! "  how: 1

roll: func [
	"Shifts a text string right or left by one character."
	text [string!] "Text to shift."
	direction [integer!] "Direction to shift -- right: 1, left: -1."
	/local h t
][
	either direction > 0 [
		h: last text  t: copy/part text ((length? text) - 1)
	][
		h: copy skip text 1  t: text/1
	]
	rejoin [h t]
]

; This next bit specifies the GUI panel. The window will have a
; gradient backdrop, over which will be composited the text, in a
; monospaced font with a drop-shadow. A timer (the 'rate' bit) is set
; to update 24 times per second. The 'engage' function in the 'feel'
; block listens for events on the text face. Time events update the
; animation and mouse-down change the animation's direction.

view layout [
	backdrop effect [gradient 0x1 coal black]

	vh1 as-is message ; 'as-is' prevents text trimming.
	font [name: font-fixed]  
	rate 24  
	feel [
		engage: func [f a e] [
			case [
				'time = a [set-face f message: roll message how] ; Animate.
				'down = a [how: how * -1] ; Change direction.
			]
		]
	]
]

Red

Red ["Animation"]

rev: false
roule: does [e: back tail s: t/text either rev [move e s] [move s e]]
view [t: text "Hello world! " rate 5 [rev: not rev] on-time [roule]]

REXX

This REXX version only works with REXX/Personal or REXX/PC.

/*REXX prg displays a text string (in one direction), and reverses when a key is pressed*/
parse upper version !ver !vernum .;     !pcRexx= 'REXX/PERSONAL'==!ver  |  'REXX/PC'==!ver
if \!pcRexx  then do
                  say
                  say '***error***  This REXX program requires REXX/PERSONAL or REXX/PC.'
                  say
                  exit 1
                  end
parse arg $                                      /*obtain optional text message from CL.*/
if $=''  then $= 'Hello World!'                  /*Not specified?  Then use the default.*/
if right($, 1)\==' '  then $= $' '               /*ensure msg text has a trailing blank.*/
signal on halt                                   /*handle a HALT if user quits this way.*/
way = 0                                          /*default direction for marquee display*/
                  y =
        do  until y=='Q';  'CLS'                 /*if user presses  Q  or  q, then quit.*/
        call lineout ,$;   call delay .2         /*display output; delay 1/5 of a second*/
        y= inKey('Nowait');  upper y             /*maybe get a pressed key; uppercase it*/
        if y\==''  then way= \way                /*change the direction of the marquee. */
        if way  then $= substr($, 2)left($, 1)   /*display marquee in a direction or ···*/
                else $=  right($, 1)substr($, 1, length($) - 1)        /* ··· the other·*/
        end   /*until*/
halt:                                            /*stick a fork in it,  we're all done. */


Ring

# Project : Animation

Load "guilib.ring"
load "stdlib.ring"
rotate = false

MyApp = New qApp {
              win1 = new qWidget() {
                         setwindowtitle("Hello World")
                         setGeometry(100,100,370,250)

                         lineedit1 = new qlineedit(win1) {
                                         setGeometry(10,100,350,30)
                                         lineedit1.settext(" Hello World! ")
                         myfilter = new qallevents(lineedit1)
                         myfilter.setMouseButtonPressevent("rotatetext()")
                         installeventfilter(myfilter)}
              show()}
exec()}

func rotatetext()
        rotate = not rotate
        strold = " Hello World! "
        for n = 1 to 15
                 if rotate = true
                    see "str = " + '"' + strold + '"' + nl
                    strnew = right(strold, 1) + left(strold, len(strold) - 1)
                    lineedit1.settext(strnew)
                    strold = strnew
                    sleep(1)
                 ok
                 if rotate = false
                    see "str = " + '"' + strold + '"' + nl
                    strnew = right(strold, len(strold) - 1) + left(strold, 1)
                    lineedit1.settext(strnew)
                    strold = strnew
                    sleep(1)
                 ok
        next
        see nl

Output:

str = " Hello World! "
str = "  Hello World!"
str = "!  Hello World"
str = "d!  Hello Worl"
str = "ld!  Hello Wor"
str = "rld!  Hello Wo"
str = "orld!  Hello W"
str = "World!  Hello "
str = " World!  Hello"
str = "o World!  Hell"
str = "lo World!  Hel"
str = "llo World!  He"
str = "ello World!  H"
str = "Hello World!  "
str = " Hello World! "

str = " Hello World! "
str = "Hello World!  "
str = "ello World!  H"
str = "llo World!  He"
str = "lo World!  Hel"
str = "o World!  Hell"
str = " World!  Hello"
str = "World!  Hello "
str = "orld!  Hello W"
str = "rld!  Hello Wo"
str = "ld!  Hello Wor"
str = "d!  Hello Worl"
str = "!  Hello World"
str = "  Hello World!"
str = " Hello World! "

Ruby

Translation of: Tcl
Library: Ruby/Tk
require 'tk'
$str = TkVariable.new("Hello World! ")
$dir = :right
 
def animate
  $str.value = shift_char($str.value, $dir)
  $root.after(125) {animate}
end

def shift_char(str, dir)
  case dir
  when :right then str[-1,1] + str[0..-2]
  when :left  then str[1..-1] + str[0,1]
  end
end
 
$root = TkRoot.new("title" => "Basic Animation")
 
TkLabel.new($root) do
  textvariable $str
  font "Courier 14"
  pack {side 'top'}
  bind("ButtonPress-1") {$dir = {:right=>:left,:left=>:right}[$dir]}
end
 
animate
Tk.mainloop
Library: Shoes
Shoes.app do
  @direction = 1
  @label = para "Hello World! ", :family => 'monospace'

  click {|button, left, top| @direction *= -1 if button == 1}

  animate(8) do |f| 
    t = @label.text
    @label.text = @direction > 0 ? t[-1] + t[0..-2] : t[1..-1] + t[0]
  end
end

Rust

Library: GTK

#[cfg(feature = "gtk")]
mod graphical {
    extern crate gtk;

    use self::gtk::traits::*;
    use self::gtk::{Inhibit, Window, WindowType};
    use std::ops::Not;
    use std::sync::{Arc, RwLock};

    pub fn create_window() {
        gtk::init().expect("Failed to initialize GTK");

        let window = Window::new(WindowType::Toplevel);
        window.connect_delete_event(|_, _| {
            gtk::main_quit();
            Inhibit(false)
        });
        let button = gtk::Button::new_with_label("Hello World! ");
        window.add(&button);

        let lock = Arc::new(RwLock::new(false));

        let lock_button = lock.clone();
        button.connect_clicked(move |_| {
            let mut reverse = lock_button.write().unwrap();
            *reverse = reverse.not();
        });

        let lock_thread = lock.clone();
        gtk::timeout_add(100, move || {
            let reverse = lock_thread.read().unwrap();
            let mut text = button.get_label().unwrap();
            let len = &text.len();

            if *reverse {
                let begin = &text.split_off(1);
                text.insert_str(0, begin);
            } else {
                let end = &text.split_off(len - 1);
                text.insert_str(0, end);
            }

            button.set_label(&text);

            gtk::Continue(true)
        });

        window.show_all();
        gtk::main();
    }
}


#[cfg(feature = "gtk")]
fn main() {
    graphical::create_window();
}

#[cfg(not(feature = "gtk"))]
fn main() {}

Scala

Works with: Scala version 2.8
import scala.actors.Actor.{actor, loop, reactWithin, exit}
import scala.actors.TIMEOUT
import scala.swing.{SimpleSwingApplication, MainFrame, Label}
import scala.swing.event.MouseClicked

case object Revert

object BasicAnimation extends SimpleSwingApplication {
  val label = new Label("Hello World! ")
  val rotator = actor {
    var goingRight = true
    loop {
      reactWithin(250 /*ms*/) {
        case Revert => goingRight = !goingRight
        case TIMEOUT =>
          if (goingRight)
            label.text = label.text.last + label.text.init
          else
            label.text = label.text.tail + label.text.head
        case unknown => println("Unknown message "+unknown); exit()
      }
    }
  }
  def top = new MainFrame {
    title = "Basic Animation"
    contents = label
  }
  listenTo(label.mouse.clicks) // use "Mouse" instead of "mouse" on Scala 2.7
  reactions += {
    case _ : MouseClicked => rotator ! Revert
  }
}

Scratch

Scratch is event and animation oriented, so this is a natural task for this language. This solution is hosted at the scratch website, where one can see it in action and read its code.

Solution Summary and Comments

The solution consists of two blocks of code. The main block initializes variables upon invocation and sets up a loop to display the crawling "Hello World! " message. The crawl is accomplished by manipulation of the list containing the message as individual characters. The other block of code is attached to the scratch sprite, and all it does is change the direction of the text crawl when the sprite is clicked.

I tried a couple of techniques for introducing a delay into the text crawl loop. The command "Say message for 2 seconds" resulted in poor performance; the application's response to clicks was spotty. Using the timer and waiting for it to exceed 2 seconds before resetting it and advancing the crawl worked much better. (I also tried waiting for the timer to equal 2 seconds, and unsurprisingly this resulted in an application freeze-up.)

smart BASIC

'Animation, by rbytes and Dutchman
word$="Hello World! "
'use button window with text
SET BUTTONS CUSTOM
SET BUTTONS FONT SIZE 40
DRAW COLOR 0,0,0
DO 'the button is redrawn each loop
BUTTON "anim" TEXT word$ AT 130,100
PAUSE .1
'touching the button reverses the scrolling
IF BUTTON_PRESSED("anim") THEN flag=1-flag
IF flag THEN 'shift right
  word$=RIGHT$(word$,1)&LEFT$(word$,LEN(word$)-1)
ELSE 'shift left
  word$=RIGHT$(word$,LEN(word$)-1)&LEFT$(word$,1)
ENDIF
UNTIL 0

This program can be concatenated onto a single line using ! as the concatenator:

'by rbytes and Dutchman!word$="Hello World! "!'use button window with text!
SET BUTTONS CUSTOM!SET BUTTONS FONT SIZE 40!DRAW COLOR 0,0,0!DO!'the button is redrawn each loop!BUTTON "anim" TEXT word$ AT 130,100!PAUSE .1!'touching the button reverses the scrolling!IF BUTTON_PRESSED("anim") THEN flag=1-flag!IF flag THEN!'shift right!word$=RIGHT$(word$,1)&LEFT$(word$,LEN(word$)-1)!ELSE!'shift left!word$=RIGHT$(word$,LEN(word$)-1)&LEFT$(word$,1)!ENDIF!UNTIL 0

Mr. Kibernetik, the creator of smart Basic, offered this ultra-compact one-line version:

w$="Hello World! "!1 BUTTON 0 TEXT w$ AT 0,0!PAUSE .1!IF BUTTON_PRESSED("0") THEN f=1-f!IF f THEN w$=RIGHT$(w$,1)&LEFT$(w$,LEN(w$)-1) ELSE w$=RIGHT$(w$,LEN(w$)-1)&LEFT$(w$,1)!GOTO 1

and smart Basic Forum member sarossell found a way to shorten even that! See if you can spot what is changed.

w$="Hello World! "!1 BUTTON 0 TEXT w$ AT 0,0!PAUSE .1!IF BUTTON_PRESSED("0") THEN f=1-f!k=LEN(w$)-1!IF f THEN w$=RIGHT$(w$,1)&LEFT$(w$,k) ELSE w$=RIGHT$(w$,k)&LEFT$(w$,1)!GOTO 1

Standard ML

Works with PolyML interpreter. Add loop for compilation.

open XWindows ;
open Motif ;
 
structure TTd = Thread.Thread ;
structure TTm = Thread.Mutex ;
 
val bannerWindow = fn () =>
 
let
  datatype thron  =  nothr | thr of TTd.thread ; 
  val toThr  =  fn thr x=> x;
  val on     =  ref nothr ;
  val mx     =  TTm.mutex ();
  val dim    =  {tw=77,th=14} ;
  val shell  =  XtAppInitialise      ""    "click text to start or redirect" "top" [] [ XmNwidth 320, XmNheight 60 ] ;
  val main   =  XmCreateMainWindow   shell    "main"                                  [ XmNmappedWhenManaged true ]  ;
  val canvas =  XmCreateDrawingArea  main   "drawarea"                                [ XmNwidth (#tw dim), XmNheight (#th dim)] ;
 
  val usegc  =  DefaultGC (XtDisplay canvas) ;
  val buf    =  XCreatePixmap (RootWindow (XtDisplay shell)) (Area{x=0,y=0,w = #tw dim, h= (#th dim)  }) 24 ;
  val _      =  (XSetBackground usegc 0xfffffff ;
		 XDrawImageString buf usegc (XPoint {x=0,y= (#th dim)-1 } ) "Hello World! ") ;
  val drawparts = fn pos => (
                 XCopyArea buf (XtWindow canvas) usegc ( XPoint {x=pos,y=0} ) (Area{x=0,y=0,w = (#tw dim) - pos , h= #th dim }) ;
                 XCopyArea buf (XtWindow canvas) usegc ( XPoint {x=00,y=0} ) (Area{x=  (#tw dim) - pos   ,y=0,w = pos, h= #th dim }) ;
		 XFlush (XtDisplay canvas) ) ;
 
  val direct  =  ref 1 ;
  fun shift s =  ( drawparts ( s mod (#tw dim)) ;  Posix.Process.sleep (Time.fromReal 0.1) ;   shift ( s +  (!direct))  ) ;
  val swdir   =  fn () => direct := ~ (!direct) ; 
  val finish  =  fn () =>
                    ( if !on <> nothr then  if TTd.isActive (toThr (!on)) then TTd.kill (toThr (!on)) else () else () ;
                       on := nothr );
  val movimg  =  fn ()      =>  ( finish () ; swdir () ;  on :=  thr  (TTd.fork (fn () =>   shift 0,[]) ) ) ;
  val setimg  =  fn (w,s,t) =>  ( finish () ;  drawparts  0 ; t )
in
 
  (
   XtSetCallbacks    canvas [ (XmNexposeCallback ,  setimg) , (XmNdestroyCallback, (fn (w,c,t)=>(finish();t))) ] XmNarmCallback ;
   XtAddEventHandler canvas [  ButtonPressMask ] false  (fn (w,ButtonPress a)=>  movimg ()|_=> ())  ;
   XtManageChild     canvas ;
   XtManageChild     main   ;  
   XtRealizeWidget   shell                                                                        (* add loop here to compile *)
  )
 
end;

call

bannerWindow () ;

Suneido

Window(Controller
    {
    Xmin: 50
    Ymin: 50
    New()
        {
        super(.layout())
        .txt = .FindControl('text')
        .moveTimer = SetTimer(NULL, 0, 600, .moveTimerFunc)
        }
    direction: -1
    moveTimer: false
    layout()
        {
        return #(Vert (Static 'Hello World! ', size: 12, weight: 600,
            notify:, name: 'text'))
        }
    moveTimerFunc(@unused)
        {
        str = .txt.Get()
        .txt.Set(str.Substr(1 * .direction) $ str.Substr(0, (1 * .direction)))
        }
    Static_Click()
        {
        .direction = .direction * -1
        }
    Destroy()
        {
        if .moveTimer isnt false
            {
            KillTimer(NULL, .moveTimer)
            ClearCallback(.moveTimerFunc)
            }
        super.Destroy()
        }
    })

SVG

Works with: Batik version 1.7

This animation is defined as a smooth movement rather than by moving whole characters, because that is more natural in SVG (without scripting); by characters would require 13 different text elements displayed in sequence.

<svg xmlns="http://www.w3.org/2000/svg" width="100" height="30">
    <g id="all">
        <rect width="100%" height="100%" fill="yellow"/>
        <g style="font: 18 'Times New Roman', serif; 
                  fill: black;
                  stroke: white; stroke-width: 0.001; /* workaround for Batik oddity */ ">
            <text x="0" y="20" textLength="95">Hello World!</text>
            <text x="-100" y="20" textLength="95">Hello World!</text>
            <animateMotion restart="whenNotActive" repeatCount="indefinite" dur="2s"
                           begin="0s;all.click" end="all.click"
                           from="0,0"   by="100,0"/>
            <animateMotion restart="whenNotActive" repeatCount="indefinite" dur="2s"
                           begin="all.click" end="all.click"
                           from="100,0" by="-100,0"/>
        </g>
    </g>
</svg>

(Does not work in Safari 4.0.2 because it apparently does not implement toggled animations correctly (see spec). Dreadful workaround: set the two animations to id="a" begin="0s;all.click" end="all.mousedown" and begin="a.end" end="all.click", respectively.)

Tcl

Library: Tk
package require Tk
set s "Hello World! "
set dir 0
# Periodic animation callback
proc animate {} {
    global dir s
    if {$dir} {
        set s [string range $s 1 end][string index $s 0]
    } else {
        set s [string index $s end][string range $s 0 end-1]
    }
    # We will run this code ~8 times a second (== 125ms delay)
    after 125 animate
}
# Make the label (constant width font looks better)
pack [label .l -textvariable s -font {Courier 14}]
# Make a mouse click reverse the direction
bind .l <Button-1> {set dir [expr {!$dir}]}
# Start the animation
animate

TI-89 BASIC

The requirements contain "When the user clicks on the text…". The TI-89 does not have a graphical cursor, so just for the sake of overdoing it, and to have a little more complex animation (overlapping objects), this program implements one. Use the arrow keys and ENTER to control the cursor.

rcanimat()
Prgm
  Local leftward,s,i,k,x,y

  false → leftward
  "Hello World! " → s
  0 → k      © last keypress found
  6*3 → x    © cursor position
  5 → y

  ClrIO
  While k ≠ 4360 and k ≠ 277 and k ≠ 264  © QUIT,HOME,ESC keys

    © Handle Enter key
    If k = 13 Then
      If x ≥ 40 and x < 40+6*dim(s) and y ≥ 25 and y < 35 Then © On text?
        not leftward → leftward
      ElseIf x ≥ 5 and x < 5+6*dim("[Quit]") and y ≥ 55 and y < 65 Then © On quit?
        Exit
      EndIf
    EndIf

    © Cursor movement keys
    If k=338 or k=340 or k=344 or k=337 or k=339 or k=342 or k=345 or k=348 Then
      Output y, x, " " © Blank old cursor pos
      If     k = 338 or k = 339 or k = 342 Then: y-6→y
      ElseIf k = 344 or k = 345 or k = 348 Then: y+6→y :EndIf
      If     k = 337 or k = 339 or k = 345 Then: x-6→x
      ElseIf k = 340 or k = 342 or k = 348 Then: x+6→x :EndIf
      min(max(y, 0), 64)→y
      min(max(x, 0), 152)→x
    EndIf

    © Drawing
    Output 0, 0, "Use arrows, ENTER key"
    Output 60, 5, "[Quit]"
    Output 30, 40, s
    Output y, x, "Ŵ"              © should be diamond symbol

    © Animation
    If leftward Then
      right(s, dim(s)-1) & left(s, 1) → s
    Else
      right(s, 1) & left(s, dim(s)-1) → s
    EndIf

    0 → i
    getKey() → k                  © reads most recent keypress or 0
    While i < 2 and k = 0         © Delay loop. Better solution?
      getKey() → k
      i + 1 → i
    EndWhile

  EndWhile
  DispHome
EndPrgm

Vedit macro language

It is not possible to detect mouse clicks while a macro is running. Therefore, the direction is controlled with Caps Lock key.

Buf_Switch(Buf_Free)
Win_Create(Buf_Num, 1, 1, 2, 14)
Ins_Text("Hello World! ")
#2 = Cur_Pos
Repeat(ALL) {
    if (Key_Shift_Status & 64) {
        BOL
        Block_Copy(#2-1, #2, DELETE)
    } else {
        Block_Copy(0, 1, DELETE)
    }
    EOL
    Update()
    Sleep(2)
}

Visual Basic

This example shows code that is hidden by the IDE. (Form creation is done graphically within the IDE, not at runtime.)

VERSION 5.00
Begin VB.Form Form1
   Begin VB.Timer Timer1
      Interval = 250
   End
   Begin VB.Label Label1
      AutoSize = -1  'True
      Caption  = "Hello World! "
   End
End
Attribute VB_Name = "Form1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
'Everything above this line is hidden when in the IDE.

Private goRight As Boolean

Private Sub Label1_Click()
    goRight = Not goRight
End Sub

Private Sub Timer1_Timer()
    If goRight Then
        x = Mid(Label1.Caption, 2) & Left(Label1.Caption, 1)
    Else
        x = Right(Label1.Caption, 1) & Left(Label1.Caption, Len(Label1.Caption) - 1)
    End If
    Label1.Caption = x
End Sub

Wren

Library: DOME
import "graphics" for Canvas, Color
import "dome" for Window
import "input" for Mouse

var RIGHT = true

class Animation {
    construct new() {
        Window.title = "Animation"
        _fore = Color.white
    }

    init() {
        _text = "Hello World! "
        _frame = 0
        Canvas.print(_text, 10, 10, _fore)
    }

    update() {
        _frame = _frame + 1
        if (_frame == 1200) _frame = 0
        if (!Mouse.hidden && Mouse.isButtonPressed("left")) {
            Mouse.hidden = true
            RIGHT = !RIGHT
        }
        if (_frame % 60 == 0) {
            if (RIGHT) {
                _text = _text[-1] + _text[0..-2]
            } else {
                _text = _text[1..-1] + _text[0]
            }
            Mouse.hidden = false
        }
    }

    draw(alpha) {
        Canvas.cls()
        Canvas.print(_text, 10, 10, _fore)
    }
}

var Game = Animation.new()

XPL0

include c:\cxpl\codes;
int     CpuReg, Dir, I, J;
char    Str;
string  0;                      \use zero-terminated strings, instead of MSb set
[CpuReg:= GetReg;               \provides access to 8086 CPU registers
\      0123456789012
Str:= "Hello World! ";
Clear;
Dir:= -1;                       \make string initially scroll to the right
I:= 0;                          \index to start of displayed portion of string
repeat  Cursor(0, 0);           \set cursor position to upper-left corner
        for J:= 0 to 12 do
                [ChOut(0, Str(I));  I:= I+1;  if I>12 then I:= 0];
        Sound(0, 2, 1);         \delay about 1/9 second
        I:= I+Dir;              \step starting position of displayed string
        if I<0 then I:=12;      \wraparound
        if I>12 then I:= 0;
        CpuReg:= GetReg;        \get mouse button press information
        CpuReg(0):= 5;  CpuReg(1):= 0;
        SoftInt($33);           \reverse direction if left button was pressed
        if CpuReg(1) then Dir:= -Dir;
until   KeyHit;                 \any keystroke terminates program
]

Yabasic

clear screen
open window 400, 150
backcolor 0, 0, 0
clear window

color 250, 120, 0
texto$ = "Hello world! "
l = len(texto$)
dir = 1
do
    release$ = inkey$(.25)
    if mouseb(release$) = -1 then
        dir = -dir
    end if
    clear window
    text 100, 90, texto$, "modern30"
    if dir = 1 then
        texto$ = right$(texto$, l-1) + left$(texto$, 1)
    else
        texto$ = right$(texto$, 1) + left$(texto$, l-1)
    end if
loop

ZX Spectrum Basic

Translation of: BBC BASIC

Replaces the detection of mouse click by pressing a key (Spectrum does not detect this device by default).

10 LET t$="Hello world! ": LET lt=LEN t$
20 LET direction=1
30 PRINT AT 0,0;t$
40 IF direction THEN LET t$=t$(2 TO )+t$(1): GO TO 60
50 LET t$=t$(lt)+t$( TO lt-1)
60 IF INKEY$<>"" THEN LET direction=NOT direction
70 PAUSE 5: GO TO 30