Draw a clock

From Rosetta Code
Draw a clock is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

Task: draw a clock. More specific:

  1. Draw a time keeping device. It can be a stopwatch, hourglass, sundial, a mouth counting "one thousand and one", anything. Only showing the seconds is required, e.g. a watch with just a second hand will suffice. However, it must clearly change every second, and the change must cycle every so often (one minute, 30 seconds, etc.) It must be drawn; printing a string of numbers to your terminal doesn't qualify. Both text-based and graphical drawing are OK.
  2. The clock is unlikely to be used to control space flights, so it needs not be hyper-accurate, but it should be usable, meaning if one can read the seconds off the clock, it must agree with the system clock.
  3. A clock is rarely (never?) a major application: don't be a CPU hog and poll the system timer every microsecond, use a proper timer/signal/event from your system or language instead. For a bad example, many OpenGL programs update the framebuffer in a busy loop even if no redraw is needed, which is very undesirable for this task.
  4. A clock is rarely (never?) a major application: try to keep your code simple and to the point. Don't write something too elaborate or convoluted, instead do whatever is natural, concise and clear in your language.

Key points: animate simple object; timed event; polling system resources; code clarity.

AutoHotkey

requires the GDI+ Library from http://www.autohotkey.com/forum/viewtopic.php?t=32238 this code from http://www.autohotkey.com/forum/viewtopic.php?p=231836#231836 draws a very nice clock with GDI+ <lang AHK>; gdi+ ahk analogue clock example written by derRaphael

Parts based on examples from Tic's GDI+ Tutorials and of course on his GDIP.ahk
This code has been licensed under the terms of EUPL 1.0
  1. SingleInstance, Force
  2. NoEnv

SetBatchLines, -1

Uncomment if Gdip.ahk is not in your standard library
  1. Include, Gdip.ahk

If !pToken := Gdip_Startup() {

  MsgBox, 48, gdiplus error!, Gdiplus failed to start. Please ensure you have gdiplus on your system
  ExitApp

} OnExit, Exit

SysGet, MonitorPrimary, MonitorPrimary SysGet, WA, MonitorWorkArea, %MonitorPrimary% WAWidth := WARight-WALeft WAHeight := WABottom-WATop

Gui, 1: -Caption +E0x80000 +LastFound +AlwaysOnTop +ToolWindow +OwnDialogs Gui, 1: Show, NA hwnd1 := WinExist()

ClockDiameter := 180 Width := Height := ClockDiameter + 2  ; make width and height slightly bigger to avoid cut away edges CenterX := CenterY := floor(ClockDiameter/2) ; Center x

Prepare our pGraphic so we have a 'canvas' to work upon
  hbm := CreateDIBSection(Width, Height), hdc := CreateCompatibleDC()
  obm := SelectObject(hdc, hbm), G := Gdip_GraphicsFromHDC(hdc)
  Gdip_SetSmoothingMode(G, 4)
Draw outer circle
  Diameter := ClockDiameter
  pBrush := Gdip_BrushCreateSolid(0x66008000)
  Gdip_FillEllipse(G, pBrush, CenterX-(Diameter//2), CenterY-(Diameter//2),Diameter, Diameter)
  Gdip_DeleteBrush(pBrush)
Draw inner circle
  Diameter := ceil(ClockDiameter - ClockDiameter*0.08)  ; inner circle is 8 % smaller than clock's diameter
  pBrush := Gdip_BrushCreateSolid(0x80008000)
  Gdip_FillEllipse(G, pBrush, CenterX-(Diameter//2), CenterY-(Diameter//2),Diameter, Diameter)
  Gdip_DeleteBrush(pBrush)
Draw Second Marks
  R1 := Diameter//2-1                        ; outer position
  R2 := Diameter//2-1-ceil(Diameter//2*0.05) ; inner position
  Items := 60                                ; we have 60 seconds
  pPen := Gdip_CreatePen(0xff00a000, floor((ClockDiameter/100)*1.2)) ; 1.2 % of total diameter is our pen width
  GoSub, DrawClockMarks
  Gdip_DeletePen(pPen)
Draw Hour Marks
  R1 := Diameter//2-1                       ; outer position
  R2 := Diameter//2-1-ceil(Diameter//2*0.1) ; inner position
  Items := 12                               ; we have 12 hours
  pPen := Gdip_CreatePen(0xc0008000, ceil((ClockDiameter//100)*2.3)) ; 2.3 % of total diameter is our pen width
  GoSub, DrawClockMarks
  Gdip_DeletePen(pPen)
  
  ; The OnMessage will let us drag the clock
  OnMessage(0x201, "WM_LBUTTONDOWN")
  UpdateLayeredWindow(hwnd1, hdc, WALeft+((WAWidth-Width)//2), WATop+((WAHeight-Height)//2), Width, Height)
  SetTimer, sec, 1000

sec:

prepare to empty previously drawn stuff
  Gdip_SetSmoothingMode(G, 1)   ; turn off aliasing
  Gdip_SetCompositingMode(G, 1) ; set to overdraw
  
delete previous graphic and redraw background
  Diameter := ceil(ClockDiameter - ClockDiameter*0.18)  ; 18 % less than clock's outer diameter
  
  ; delete whatever has been drawn here
  pBrush := Gdip_BrushCreateSolid(0x00000000) ; fully transparent brush 'eraser'
  Gdip_FillEllipse(G, pBrush, CenterX-(Diameter//2), CenterY-(Diameter//2),Diameter, Diameter)
  Gdip_DeleteBrush(pBrush)
  
  Gdip_SetCompositingMode(G, 0) ; switch off overdraw
  pBrush := Gdip_BrushCreateSolid(0x66008000)
  Gdip_FillEllipse(G, pBrush, CenterX-(Diameter//2), CenterY-(Diameter//2),Diameter, Diameter)
  Gdip_DeleteBrush(pBrush)
  pBrush := Gdip_BrushCreateSolid(0x80008000)
  Gdip_FillEllipse(G, pBrush, CenterX-(Diameter//2), CenterY-(Diameter//2),Diameter, Diameter)
  Gdip_DeleteBrush(pBrush)
  
Draw HoursPointer
  Gdip_SetSmoothingMode(G, 4)   ; turn on antialiasing
  t := A_Hour*360//12 + (A_Min*360//60)//12 +90 
  R1 := ClockDiameter//2-ceil((ClockDiameter//2)*0.5) ; outer position
  pPen := Gdip_CreatePen(0xa0008000, floor((ClockDiameter/100)*3.5))
  Gdip_DrawLine(G, pPen, CenterX, CenterY
     , ceil(CenterX - (R1 * Cos(t * Atan(1) * 4 / 180)))
     , ceil(CenterY - (R1 * Sin(t * Atan(1) * 4 / 180))))
  Gdip_DeletePen(pPen)
  
Draw MinutesPointer
  t := A_Min*360//60+90 
  R1 := ClockDiameter//2-ceil((ClockDiameter//2)*0.25) ; outer position
  pPen := Gdip_CreatePen(0xa0008000, floor((ClockDiameter/100)*2.7))
  Gdip_DrawLine(G, pPen, CenterX, CenterY
     , ceil(CenterX - (R1 * Cos(t * Atan(1) * 4 / 180)))
     , ceil(CenterY - (R1 * Sin(t * Atan(1) * 4 / 180))))
  Gdip_DeletePen(pPen)
Draw SecondsPointer
  t := A_Sec*360//60+90 
  R1 := ClockDiameter//2-ceil((ClockDiameter//2)*0.2) ; outer position
  pPen := Gdip_CreatePen(0xa000FF00, floor((ClockDiameter/100)*1.2))
  Gdip_DrawLine(G, pPen, CenterX, CenterY
     , ceil(CenterX - (R1 * Cos(t * Atan(1) * 4 / 180)))
     , ceil(CenterY - (R1 * Sin(t * Atan(1) * 4 / 180))))
  Gdip_DeletePen(pPen)
  
  UpdateLayeredWindow(hwnd1, hdc) ;, xPos, yPos, ClockDiameter, ClockDiameter)

return

DrawClockMarks:

  Loop, % Items
     Gdip_DrawLine(G, pPen
        , CenterX - ceil(R1 * Cos(((a_index-1)*360//Items) * Atan(1) * 4 / 180))
        , CenterY - ceil(R1 * Sin(((a_index-1)*360//Items) * Atan(1) * 4 / 180))
        , CenterX - ceil(R2 * Cos(((a_index-1)*360//Items) * Atan(1) * 4 / 180))
        , CenterY - ceil(R2 * Sin(((a_index-1)*360//Items) * Atan(1) * 4 / 180)) )

return

WM_LBUTTONDOWN() {

  PostMessage, 0xA1, 2
  return

}

esc:: Exit:

  SelectObject(hdc, obm)
  DeleteObject(hbm)
  DeleteDC(hdc)
  Gdip_DeleteGraphics(G)
  Gdip_Shutdown(pToken)
  ExitApp

Return</lang>


C

Draws a crude clock in terminal. C99, compiled with gcc -std=c99. <lang C>#include <stdio.h>

  1. include <stdlib.h>
  2. include <math.h>
  3. include <time.h>
  4. include <sys/time.h>
  1. define PI 3.14159265

const char * shades = " .:-*ca&#%@";

/* distance of (x, y) from line segment (0, 0)->(x0, y0) */ double dist(double x, double y, double x0, double y0) { double l = (x * x0 + y * y0) / (x0 * x0 + y0 * y0);

if (l > 1) { x -= x0; y -= y0; } else if (l >= 0) { x -= l * x0; y -= l * y0; } return sqrt(x * x + y * y); }

enum { sec = 0, min, hur }; // for subscripts

void draw(int size) {

  1. define for_i for(int i = 0; i < size; i++)
  2. define for_j for(int j = 0; j < size * 2; j++)

double angle, cx = size / 2.; double sx[3], sy[3], sw[3]; double fade[] = { 1, .35, .35 }; /* opacity of each arm */ struct timeval tv; struct tm *t;

/* set width of each arm */ sw[sec] = size * .02; sw[min] = size * .03; sw[hur] = size * .05;

every_second: gettimeofday(&tv, 0); t = localtime(&tv.tv_sec);

angle = t->tm_sec * PI / 30; sy[sec] = -cx * cos(angle); sx[sec] = cx * sin(angle);

angle = (t->tm_min + t->tm_sec / 60.) / 30 * PI; sy[min] = -cx * cos(angle) * .8; sx[min] = cx * sin(angle) * .8;

angle = (t->tm_hour + t->tm_min / 60.) / 6 * PI; sy[hur] = -cx * cos(angle) * .6; sx[hur] = cx * sin(angle) * .6;

printf("\033[s"); /* save cursor position */ for_i { printf("\033[%d;0H", i); /* goto row i, col 0 */ double y = i - cx; for_j { double x = (j - 2 * cx) / 2;

int pix = 0; /* calcs how far the "pixel" is from each arm and set * shade, with some anti-aliasing. It's ghetto, but much * easier than a real scanline conversion. */ for (int k = hur; k >= sec; k--) { double d = dist(x, y, sx[k], sy[k]); if (d < sw[k] - .5) pix = 10 * fade[k]; else if (d < sw[k] + .5) pix = (5 + (sw[k] - d) * 10) * fade[k]; } putchar(shades[pix]); } } printf("\033[u"); /* restore cursor pos so you can bg the job -- value unclear */

fflush(stdout); sleep(1); /* sleep 1 can at times miss a second, but will catch up next update */ goto every_second; }

int main(int argc, char *argv[]) { int s; if (argc <= 1 || (s = atoi(argv[1])) <= 0) s = 20; draw(s); return 0; }</lang>


C++

<lang cpp>

  1. include <windows.h>
  2. include <string>
  3. include <math.h>

//-------------------------------------------------------------------------------------------------- using namespace std;

//-------------------------------------------------------------------------------------------------- const int BMP_SIZE = 300, MY_TIMER = 987654, CENTER = BMP_SIZE >> 1, SEC_LEN = CENTER - 20,

         MIN_LEN = SEC_LEN - 20, HOUR_LEN = MIN_LEN - 20;

const float PI = 3.1415926536f;

//-------------------------------------------------------------------------------------------------- class vector2 { public:

   vector2() { x = y = 0; }
   vector2( int a, int b ) { x = a; y = b; }
   void set( int a, int b ) { x = a; y = b; }
   void rotate( float angle_r )
   {

float _x = static_cast<float>( x ), _y = static_cast<float>( y ), s = sinf( angle_r ), c = cosf( angle_r ), a = _x * c - _y * s, b = _x * s + _y * c;

x = static_cast<int>( a ); y = static_cast<int>( b );

   }
   int x, y;

}; //-------------------------------------------------------------------------------------------------- class myBitmap { public:

   myBitmap() : pen( NULL ), brush( NULL ), clr( 0 ), wid( 1 ) {}
   ~myBitmap()
   {

DeleteObject( pen ); DeleteObject( brush ); DeleteDC( hdc ); DeleteObject( bmp );

   }
   bool create( int w, int h )
   {

BITMAPINFO bi; ZeroMemory( &bi, sizeof( bi ) ); bi.bmiHeader.biSize = sizeof( bi.bmiHeader ); bi.bmiHeader.biBitCount = sizeof( DWORD ) * 8; bi.bmiHeader.biCompression = BI_RGB; bi.bmiHeader.biPlanes = 1; bi.bmiHeader.biWidth = w; bi.bmiHeader.biHeight = -h;

HDC dc = GetDC( GetConsoleWindow() ); bmp = CreateDIBSection( dc, &bi, DIB_RGB_COLORS, &pBits, NULL, 0 ); if( !bmp ) return false;

hdc = CreateCompatibleDC( dc ); SelectObject( hdc, bmp ); ReleaseDC( GetConsoleWindow(), dc );

width = w; height = h; return true;

   }
   void clear( BYTE clr = 0 )
   {

memset( pBits, clr, width * height * sizeof( DWORD ) );

   }
   void setBrushColor( DWORD bClr )
   {

if( brush ) DeleteObject( brush ); brush = CreateSolidBrush( bClr ); SelectObject( hdc, brush );

   }
   void setPenColor( DWORD c )
   {

clr = c; createPen();

   }
   void setPenWidth( int w )
   {

wid = w; createPen();

   }
   void saveBitmap( string path )
   {

BITMAPFILEHEADER fileheader; BITMAPINFO infoheader; BITMAP bitmap; DWORD wb;

GetObject( bmp, sizeof( bitmap ), &bitmap ); DWORD* dwpBits = new DWORD[bitmap.bmWidth * bitmap.bmHeight];

       ZeroMemory( dwpBits, bitmap.bmWidth * bitmap.bmHeight * sizeof( DWORD ) );

ZeroMemory( &infoheader, sizeof( BITMAPINFO ) ); ZeroMemory( &fileheader, sizeof( BITMAPFILEHEADER ) );

infoheader.bmiHeader.biBitCount = sizeof( DWORD ) * 8; infoheader.bmiHeader.biCompression = BI_RGB; infoheader.bmiHeader.biPlanes = 1; infoheader.bmiHeader.biSize = sizeof( infoheader.bmiHeader ); infoheader.bmiHeader.biHeight = bitmap.bmHeight; infoheader.bmiHeader.biWidth = bitmap.bmWidth; infoheader.bmiHeader.biSizeImage = bitmap.bmWidth * bitmap.bmHeight * sizeof( DWORD );

fileheader.bfType = 0x4D42; fileheader.bfOffBits = sizeof( infoheader.bmiHeader ) + sizeof( BITMAPFILEHEADER ); fileheader.bfSize = fileheader.bfOffBits + infoheader.bmiHeader.biSizeImage;

GetDIBits( hdc, bmp, 0, height, ( LPVOID )dwpBits, &infoheader, DIB_RGB_COLORS );

HANDLE file = CreateFile( path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); WriteFile( file, &fileheader, sizeof( BITMAPFILEHEADER ), &wb, NULL ); WriteFile( file, &infoheader.bmiHeader, sizeof( infoheader.bmiHeader ), &wb, NULL ); WriteFile( file, dwpBits, bitmap.bmWidth * bitmap.bmHeight * 4, &wb, NULL ); CloseHandle( file );

delete [] dwpBits;

   }
   HDC getDC() const     { return hdc; }
   int getWidth() const  { return width; }
   int getHeight() const { return height; }

private:

   void createPen()
   {

if( pen ) DeleteObject( pen ); pen = CreatePen( PS_SOLID, wid, clr ); SelectObject( hdc, pen );

   }
   HBITMAP bmp;
   HDC     hdc;
   HPEN    pen;
   HBRUSH  brush;
   void    *pBits;
   int     width, height, wid;
   DWORD   clr;

}; //-------------------------------------------------------------------------------------------------- class clock { public:

   clock()  
   {

_bmp.create( BMP_SIZE, BMP_SIZE ); _bmp.clear( 100 ); _bmp.setPenWidth( 2 ); _ang = DegToRadian( 6 );

   }
   void setNow()
   {

GetLocalTime( &_sysTime ); draw();

   }
   float DegToRadian( float degree ) { return degree * ( PI / 180.0f ); }
   void setHWND( HWND hwnd ) { _hwnd = hwnd; }

private:

   void drawTicks( HDC dc )
   {

vector2 line; _bmp.setPenWidth( 1 ); for( int x = 0; x < 60; x++ ) { line.set( 0, 50 ); line.rotate( static_cast<float>( x + 30 ) * _ang ); MoveToEx( dc, CENTER - static_cast<int>( 2.5f * static_cast<float>( line.x ) ), CENTER - static_cast<int>( 2.5f * static_cast<float>( line.y ) ), NULL ); LineTo( dc, CENTER - static_cast<int>( 2.81f * static_cast<float>( line.x ) ), CENTER - static_cast<int>( 2.81f * static_cast<float>( line.y ) ) ); }

_bmp.setPenWidth( 3 ); for( int x = 0; x < 60; x += 5 ) { line.set( 0, 50 ); line.rotate( static_cast<float>( x + 30 ) * _ang ); MoveToEx( dc, CENTER - static_cast<int>( 2.5f * static_cast<float>( line.x ) ), CENTER - static_cast<int>( 2.5f * static_cast<float>( line.y ) ), NULL ); LineTo( dc, CENTER - static_cast<int>( 2.81f * static_cast<float>( line.x ) ), CENTER - static_cast<int>( 2.81f * static_cast<float>( line.y ) ) ); }

   }
   void drawHands( HDC dc )
   {

float hp = DegToRadian( ( 30.0f * static_cast<float>( _sysTime.wMinute ) ) / 60.0f ); int h = ( _sysTime.wHour > 12 ? _sysTime.wHour - 12 : _sysTime.wHour ) * 5;

_bmp.setPenWidth( 3 ); _bmp.setPenColor( RGB( 0, 0, 255 ) ); drawHand( dc, HOUR_LEN, ( _ang * static_cast<float>( 30 + h ) ) + hp );

_bmp.setPenColor( RGB( 0, 128, 0 ) ); drawHand( dc, MIN_LEN, _ang * static_cast<float>( 30 + _sysTime.wMinute ) );

_bmp.setPenWidth( 2 ); _bmp.setPenColor( RGB( 255, 0, 0 ) ); drawHand( dc, SEC_LEN, _ang * static_cast<float>( 30 + _sysTime.wSecond ) );

   }
   void drawHand( HDC dc, int len, float ang )
   {

vector2 line; line.set( 0, len ); line.rotate( ang ); MoveToEx( dc, CENTER, CENTER, NULL ); LineTo( dc, line.x + CENTER, line.y + CENTER );

   }
   void draw()
   {

HDC dc = _bmp.getDC();

_bmp.setBrushColor( RGB( 250, 250, 250 ) ); Ellipse( dc, 0, 0, BMP_SIZE, BMP_SIZE ); _bmp.setBrushColor( RGB( 230, 230, 230 ) ); Ellipse( dc, 10, 10, BMP_SIZE - 10, BMP_SIZE - 10 );

drawTicks( dc ); drawHands( dc );

_bmp.setPenColor( 0 ); _bmp.setBrushColor( 0 ); Ellipse( dc, CENTER - 5, CENTER - 5, CENTER + 5, CENTER + 5 );

_wdc = GetDC( _hwnd ); BitBlt( _wdc, 0, 0, BMP_SIZE, BMP_SIZE, dc, 0, 0, SRCCOPY ); ReleaseDC( _hwnd, _wdc );

   }
   myBitmap   _bmp;
   HWND       _hwnd;
   HDC        _wdc;
   SYSTEMTIME _sysTime;
   float      _ang;

}; //-------------------------------------------------------------------------------------------------- class wnd { public:

   wnd() { _inst = this; }
   int wnd::Run( HINSTANCE hInst )
   {

_hInst = hInst; _hwnd = InitAll(); SetTimer( _hwnd, MY_TIMER, 1000, NULL ); _clock.setHWND( _hwnd );

ShowWindow( _hwnd, SW_SHOW ); UpdateWindow( _hwnd );

MSG msg; ZeroMemory( &msg, sizeof( msg ) ); while( msg.message != WM_QUIT ) { if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) != 0 ) { TranslateMessage( &msg ); DispatchMessage( &msg ); } } return UnregisterClass( "_MY_CLOCK_", _hInst );

   }

private:

   void wnd::doPaint( HDC dc ) { _clock.setNow(); }
   void wnd::doTimer()         { _clock.setNow(); }
   static int WINAPI wnd::WndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
   {

switch( msg ) { case WM_DESTROY: PostQuitMessage( 0 ); break; case WM_PAINT: { PAINTSTRUCT ps; HDC dc = BeginPaint( hWnd, &ps ); _inst->doPaint( dc ); EndPaint( hWnd, &ps ); return 0; } case WM_TIMER: _inst->doTimer(); break; default: return DefWindowProc( hWnd, msg, wParam, lParam ); } return 0;

   }
   HWND InitAll()
   {

WNDCLASSEX wcex; ZeroMemory( &wcex, sizeof( wcex ) ); wcex.cbSize = sizeof( WNDCLASSEX ); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = ( WNDPROC )WndProc; wcex.hInstance = _hInst; wcex.hCursor = LoadCursor( NULL, IDC_ARROW ); wcex.hbrBackground = ( HBRUSH )( COLOR_WINDOW + 1 ); wcex.lpszClassName = "_MY_CLOCK_";

RegisterClassEx( &wcex );

RECT rc = { 0, 0, BMP_SIZE, BMP_SIZE }; AdjustWindowRect( &rc, WS_SYSMENU | WS_CAPTION, FALSE ); int w = rc.right - rc.left, h = rc.bottom - rc.top; return CreateWindow( "_MY_CLOCK_", ".: Clock -- PJorente :.", WS_SYSMENU, CW_USEDEFAULT, 0, w, h, NULL, NULL, _hInst, NULL );

   }
   static wnd* _inst;
   HINSTANCE  _hInst;
   HWND       _hwnd;
   clock      _clock;

}; wnd* wnd::_inst = 0; //-------------------------------------------------------------------------------------------------- int APIENTRY _tWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow ) {

   wnd myWnd;
   return myWnd.Run( hInstance );

} //-------------------------------------------------------------------------------------------------- </lang>

GUISS

<lang guiss>Start,Programs,Accessories,Analogue Clock</lang>

JavaScript

Tested on Gecko. Put the following in a <script> tag somewhere, and call init_clock() after body load. <lang JavaScript>var sec_old = 0; function update_clock() { var t = new Date(); var arms = [t.getHours(), t.getMinutes(), t.getSeconds()]; if (arms[2] == sec_old) return; sec_old = arms[2];

var c = document.getElementById('clock'); var ctx = c.getContext('2d'); ctx.fillStyle = "rgb(0,200,200)"; ctx.fillRect(0, 0, c.width, c.height); ctx.fillStyle = "white"; ctx.fillRect(3, 3, c.width - 6, c.height - 6); ctx.lineCap = 'round';

var orig = { x: c.width / 2, y: c.height / 2 }; arms[1] += arms[2] / 60; arms[0] += arms[1] / 60; draw_arm(ctx, orig, arms[0] * 30, c.width/2.5 - 15, c.width / 20, "green"); draw_arm(ctx, orig, arms[1] * 6, c.width/2.2 - 10, c.width / 30, "navy"); draw_arm(ctx, orig, arms[2] * 6, c.width/2.0 - 6, c.width / 100, "maroon"); }

function draw_arm(ctx, orig, deg, len, w, style) { ctx.save(); ctx.lineWidth = w; ctx.lineCap = 'round'; ctx.translate(orig.x, orig.y); ctx.rotate((deg - 90) * Math.PI / 180); ctx.strokeStyle = style; ctx.beginPath(); ctx.moveTo(-len / 10, 0); ctx.lineTo(len, 0); ctx.stroke(); ctx.restore(); }

function init_clock() { var clock = document.createElement('canvas'); clock.width = 100; clock.height = 100; clock.id = "clock"; document.body.appendChild(clock);

window.setInterval(update_clock, 200); }</lang>

Liberty BASIC

LB has a timer to call a routine at regular intervals. The example is a cut-down version of the full clock supplied with LB as an example. <lang lb>

   WindowWidth  =120
   WindowHeight =144
   nomainwin
   open "Clock" for graphics_nsb_nf as #clock
   #clock "trapclose [exit]"
   #clock "fill white"
   for angle =0 to 330 step 30
       #clock "up ; home ; north ; turn "; angle
       #clock "go 40 ; down ; go 5"
   next angle
   #clock "flush"
   timer 1000, [display]
   wait

[display] ' called only when seconds have changed

   time$   =time$()
   seconds =val( right$( time$, 2))
   ' delete the last drawn segment, if there is one
   if segId >2 then #clock "delsegment "; segId -1
   ' center the turtle
   #clock "up ; home ; down ; north"
   ' erase each hand if its position has changed
   if oldSeconds <>seconds then #clock, "size 1 ; color white ; turn "; oldSeconds *6 ; " ; go 38 ; home ; color black ; north" : oldSeconds =seconds
   ' redraw all three hands, second hand first
   #clock "size 1 ; turn "; seconds * 6 ; " ; go 38"
   ' flush to end segment, then get the next segment id #
   #clock "flush"
   #clock "segment"
   input #clock, segId
   wait

[exit]

   close #clock
   end

</lang>

Locomotive Basic

Draw a clock with just a second hand:

<lang locobasic>10 mode 1:defint a-z:deg 20 r=150:s=0:p=0 30 origin 320,200 40 move 0,r 50 for a=0 to 360 step 2 60 draw r*sin(a),r*cos(a) 70 next 80 for a=0 to 360 step 6 90 move .9*r*sin(a),.9*r*cos(a) 100 draw r*sin(a),r*cos(a) 110 next 120 every 50 gosub 160 130 ' main loop 140 goto 140 150 ' draw clock 160 gosub 230 170 s=(s+1) mod 60 180 locate 1,1 190 print s 200 gosub 230 210 return 220 ' draw hand 230 a=s/60*360 240 move 0,0 250 frame:draw .8*r*sin(a),.8*r*cos(a),p 260 p=1-p 270 return</lang>

Mathematica

<lang Mathematica>makeHand[fl_, bl_, fw_, bw_] := Polygon[{{-bw, -bl}, {bw, -bl}, {fw, fl}, {0, fl + 8 fw}, {-fw, fl}}/9];

hourHand = makeHand[5, 5/3, .1, .3];minuteHand = makeHand[7, 7/3, .1, .3]; secondHand = {Red, EdgeForm[Black], makeHand[7, 7/3, .1/2, .3/2]};

Graphics[{

 {Thickness[.03], Circle[]},(* Rim *)
 {Thickness[.003], Table[Line[{.9 {Cos[a], Sin[a]}, .95 {Cos[a], Sin[a]}}], {a, 0, 2 \[Pi], 2 \[Pi]/60}]}, (* Thin ticks *)
 {Thickness[.01], Table[Line[{.9 {Cos[a], Sin[a]}, .95 {Cos[a], Sin[a]}}], {a, 0, 2 \[Pi], 2 \[Pi]/12}]}, (* Thick ticks *)
 Style[Table[Text[i, .77 {Cos[-i \[Pi]/6 + \[Pi]/2], Sin[-i \[Pi]/6 + \[Pi]/2]}], {i, 1, 12}], FontFamily -> "Helvetica", FontSize -> 36], (* Numbers *)
 Rotate[hourHand, Dynamic[Refresh[-30 Mod[AbsoluteTime[]/3600, 60] \[Degree], UpdateInterval -> 60]], {0, 0}],
 Rotate[minuteHand, Dynamic[Refresh[-6 Mod[AbsoluteTime[]/60, 60] \[Degree], UpdateInterval -> 1]], {0, 0}],
 Rotate[secondHand, Dynamic[Refresh[-6 Mod[AbsoluteTime[], 60] \[Degree], UpdateInterval -> 1/20]], {0, 0}]
 }]</lang>

MATLAB / Octave

<lang Matlab> u = [0:360]*pi/180;

 while(1) 
    s = mod(now*60*24,1)*2*pi;
    plot([0,sin(s)],[0,cos(s)],'-',sin(u),cos(u),'k-');
    pause(1);
 end;</lang>

OCaml

Using only the standard library of OCaml with its Graphics module:

<lang ocaml>#!/usr/bin/env ocaml

  1. load "unix.cma"
  2. load "graphics.cma"

open Graphics

let pi = 4.0 *. atan 1.0 let angle v max = float v /. max *. 2.0 *. pi

let () =

 open_graph "";
 set_window_title "OCaml Clock";
 resize_window 256 256;
 auto_synchronize false;
 let w = size_x ()
 and h = size_y () in
 let rec loop () =
   clear_graph ();
   let point radius r a =
     let x = int_of_float (radius *. sin a)
     and y = int_of_float (radius *. cos a) in
     fill_circle (w/2+x) (h/2+y) r;
   in
   set_color (rgb 192 192 192);
   point 84.0 8 0.0;
   point 84.0 8 (angle  90 360.0);
   point 84.0 8 (angle 180 360.0);
   point 84.0 8 (angle 270 360.0);
   set_color (rgb 224 224 224);
   point 84.0 6 (angle  30 360.0);
   point 84.0 6 (angle  60 360.0);
   point 84.0 6 (angle 120 360.0);
   point 84.0 6 (angle 150 360.0);
   point 84.0 6 (angle 210 360.0);
   point 84.0 6 (angle 240 360.0);
   point 84.0 6 (angle 300 360.0);
   point 84.0 6 (angle 330 360.0);
   set_line_width 9;
   set_color (rgb 192 192 192);
   draw_circle (w/2) (h/2) 100;
   let tm = Unix.localtime (Unix.gettimeofday ()) in
   let sec = angle tm.Unix.tm_sec 60.0 in
   let min = angle tm.Unix.tm_min 60.0 in
   let hour = angle (tm.Unix.tm_hour * 60 + tm.Unix.tm_min) (24.0 *. 60.0) in
   let hour = hour *. 2.0 in
   let hand t radius width color =
     let x = int_of_float (radius *. sin t)
     and y = int_of_float (radius *. cos t) in
     set_line_width width;
     set_color color;
     moveto (w/2) (h/2);  rlineto x y;
   in
   hand sec  90.0 2 (rgb 0 128 255);
   hand min  82.0 4 (rgb 0 0 128);
   hand hour 72.0 6 (rgb 255 0 128);
   synchronize ();
   Unix.sleep 1;
   loop ()
 in
 try loop ()
 with _ -> close_graph ()</lang>



GTK + Cairo

Library: ocaml-cairo
Library: LablGTK2

Using the libraries GTK2 and Cairo and their OCaml bindings LablGTK and ocaml-cairo.

# compile with:
ocamlopt -I +lablgtk2 -I +cairo -o gtkclock.opt \
         unix.cmxa lablgtk.cmxa cairo.cmxa cairo_lablgtk.cmxa gtkInit.cmx gtkclock.ml

<lang ocaml>let pi = 4.0 *. atan 1.0 let angle v max = float v /. max *. 2.0 *. pi

let draw area _ =

 let cr = Cairo_lablgtk.create area#misc#window in
 let { Gtk.width = width; Gtk.height = height } = area#misc#allocation in
 let scale p = float (min width height) *. 0.5 *. p in
 let center_x, center_y = float width /. 2.0, float height /. 2.0 in
 let invert_y y = float height -. y in
 Cairo.set_source_rgb cr 0.8 0.8 0.8;
 Cairo.paint cr;  (* background *)
 Cairo.set_source_rgb cr 1.0 1.0 1.0;
 Cairo.arc cr center_x center_y (scale 0.9) 0.0 (2.0 *. pi);
 Cairo.set_line_width cr (scale 0.02);
 Cairo.stroke cr;
 let point a =
   let radius = (scale 0.9) in
   let x = radius *. sin a
   and y = radius *. cos a in
   let r = scale 0.04 in
   Cairo.arc cr (center_x +. x) (invert_y (center_y +. y)) r 0.0 (2.0 *. pi);
   Cairo.fill cr;
 in
 for i = 0 to pred 12 do
   point (angle (i * 30) 360.0)
 done;
 let tm = Unix.localtime (Unix.gettimeofday ()) in
 let sec = angle tm.Unix.tm_sec 60.0 in
 let min = angle tm.Unix.tm_min 60.0 in
 let hour = angle (tm.Unix.tm_hour * 60 + tm.Unix.tm_min) (12.0 *. 60.0) in
 Cairo.set_line_cap cr Cairo.LINE_CAP_ROUND;
 let hand t radius lwidth (r, g, b) =
   let x = radius *. sin t
   and y = radius *. cos t in
   Cairo.set_line_width cr (scale lwidth);
   Cairo.move_to cr center_x center_y;
   Cairo.line_to cr (center_x +. x) (invert_y (center_y +. y));
   Cairo.set_source_rgb cr r g b;
   Cairo.stroke cr;
 in
 hand sec  (scale 0.9) 0.04 (0.0, 0.5, 1.0);
 hand min  (scale 0.7) 0.06 (0.0, 0.0, 0.5);
 hand hour (scale 0.6) 0.09 (1.0, 0.0, 0.5);
 true

let animate area =

 ignore (GMain.Timeout.add 200 (fun () ->
   GtkBase.Widget.queue_draw area#as_widget; true))

let () =

 let w = GWindow.window ~title:"OCaml GtkCairo Clock" () in
 ignore (w#connect#destroy GMain.quit);
 let f = GBin.frame ~shadow_type:`IN ~packing:w#add () in
 let area = GMisc.drawing_area ~width:200 ~height:200 ~packing:f#add () in
 area#misc#set_double_buffered true;
 ignore (area#event#connect#expose (draw area));
 animate area;
 w#show ();
 GMain.main ()</lang>

PicoLisp

This is an animated ASCII drawing of the "Berlin-Uhr", a clock built to display the time according to the principles of set theory, which is installed in Berlin since 1975. See www.surveyor.in-berlin.de/berlin/uhr/indexe.html and www.cs.utah.edu/~hatch/berlin_uhr.html. <lang PicoLisp>(de draw Lst

  (for L Lst
     (for X L
        (cond
           ((num? X) (space X))
           ((sym? X) (prin X))
           (T (do (car X) (prin (cdr X)))) ) )
     (prinl) ) )

(de bigBox (N)

  (do 2
     (prin "|")
     (for I 4
        (prin (if (> I N) "          |" " ======== |")) )
     (prinl) ) )

(call 'clear) # Clear screen (call "tput" "civis") # Set cursor invisible

(push '*Bye '(call "tput" "cnorm")) # Set cursor visible on exit

(loop

  (call "tput" "cup" 0 0)  # Cursor to top left
  (let Time (time (time))
     (draw (20 (5 . _)) (19 / 5 \\))
     (if (onOff (NIL))
        (draw (18 / 7 \\) (18 \\ 7 /))
        (draw (18 / 2 (3 . "#") 2 \\) (18 \\ 2 (3 . "#") 2 /)) )
     (draw
        (19 \\ (5 . _) /)
        (+ (10 . -) + (10 . -) + (10 . -) + (10 . -) +) )
     (bigBox (/ (car Time) 5))
     (draw (+ (10 . -) + (10 . -) + (10 . -) + (10 . -) +))
     (bigBox (% (car Time) 5))
     (draw (+ (43 . -) +))
     (do 2
        (prin "|")
        (for I `(range 5 55 5)
           (prin
              (cond
                 ((> I (cadr Time)) "   |")
                 ((=0 (% I 3)) " # |")
                 (T " = |") ) ) )
        (prinl) )
     (draw (+ (43 . -) +))
     (bigBox (% (cadr Time) 5))
     (draw (+ (10 . -) + (10 . -) + (10 . -) + (10 . -) +)) )
  (wait 1000) )</lang>

The six '#' characters in the "circle" on top toggle on/off every second. This is the display at 17:46:

                    _____
                   /     \
                  /  ###  \
                  \  ###  /
                   \_____/
+----------+----------+----------+----------+
| ======== | ======== | ======== |          |
| ======== | ======== | ======== |          |
+----------+----------+----------+----------+
| ======== | ======== |          |          |
| ======== | ======== |          |          |
+-------------------------------------------+
| = | = | # | = | = | # | = | = | # |   |   |
| = | = | # | = | = | # | = | = | # |   |   |
+-------------------------------------------+
| ======== |          |          |          |
| ======== |          |          |          |
+----------+----------+----------+----------+

PureBasic

Sample display of PureBasic solution

<lang purebasic>#MiddleX = 90 + 1 ;x,y must be odd numbers, minimum width is 67

  1. MiddleY = #MiddleX
  2. len_sh = (#MiddleX - 8) * 0.97 ;length of second-hand
  3. len_mh = (#MiddleX - 8) * 0.88 ;length of minute-hand
  4. len_hh = (#MiddleX - 8) * 0.66 ;length of hour-hand
  5. clockFace_img = 0
  6. clock_gad = 0
  7. clock_win = 0

Define cx = #MiddleX, cy = #MiddleY, i, ri.f Define c_gray = RGB($CC, $CC, $CC), c_mgray = RGB($99, $99, $99) Define c_white = RGB(255, 255, 255), c_black =RGB(0, 0, 0) Define c_red = RGB(255, 0, 0), c_blue = RGB(0, 0, 255) Define c_dcyan = RGB($27, $BC, $D8), c_lgreen = RGB($60, $E0, $9) Define c_yellow = RGB($F4, $D5, $0B)

CreateImage(#clockFace_img, cx * 2 - 1, cy * 2 - 1) StartDrawing(ImageOutput(#clockFace_img))

 Box(0, 0, cx * 2 - 1, cy * 2 - 1, c_mgray)
 Circle(cx, cy, cx - 2, c_dcyan)
 For i = 0 To 359 Step 30
   ri = Radian(i)
   Circle(cx + Sin(ri) * (cx - 5), cy + Cos(ri) * (cx - 5), 3, c_gray)
 Next

StopDrawing() OpenWindow(#clock_win, 0, 0, cx * 2, cy * 2, "Clock") ImageGadget(#clock_gad, 0, 0, cx * 2, cy * 2, ImageID(#clockFace_img))

Define x, y, rad_s.f, rad_m.f, rad_h.f, t$ Repeat

 event = WaitWindowEvent(25)
 If event = 0
   rad_s = Radian(360 - (Second(Date()) * 6) + 180)
   rad_m = Radian(360 - (Minute(Date()) * 6) + 180)
   rad_h = Radian(360 - (((Hour(Date()) - 1) * 30) + 180) - (Minute(Date()) / 2))
   StartDrawing(ImageOutput(#clockFace_img))
     Circle(cx, cy, cx - 8, c_lgreen)
     t$ = FormatDate("%mm-%dd-%yyyy", Date())
     x = cx - (TextWidth(t$) + 2) / 2
     y = (cy - (TextHeight(t$) + 2) - 4) / 2
     Box(x, y, TextWidth(t$) + 2, TextHeight(t$) + 2, c_black)
     DrawText(x + 2, y + 2, t$, c_black, c_yellow)
     LineXY(cx, cy, cx + Sin(rad_s) * #len_sh, cy + Cos(rad_s) * #len_sh, c_white)
     LineXY(cx, cy, cx + Sin(rad_m) * #len_mh, cy + Cos(rad_m) * #len_mh, c_red)
     LineXY(cx, cy, cx + Sin(rad_h) * #len_hh, cy + Cos(rad_h) * #len_hh, c_black)
     Circle(cx, cy, 4, c_blue)
   StopDrawing()
   SetGadgetState(#clock_gad, ImageID(#clockFace_img))
 EndIf 

Until event = #PB_Event_CloseWindow</lang>

Python

Think Geek Binary Clock

Works with: Python version 2.6+, 3.0+

<lang python>import time

def chunks(l, n=5):

   return [l[i:i+n] for i in range(0, len(l), n)]

def binary(n, digits=8):

   n=int(n)
   return '{0:0{1}b}'.format(n, digits)

def secs(n):

   n=int(n)
   h='x' * n
   return "|".join(chunks(h))

def bin_bit(h):

   h=h.replace("1","x")
   h=h.replace("0"," ")
   return "|".join(list(h))


x=str(time.ctime()).split() y=x[3].split(":")

s=y[-1] y=map(binary,y[:-1])

print bin_bit(y[0]) print print bin_bit(y[1]) print print secs(s)</lang>

Racket

Draws an analog clock in a new GUI window:

<lang racket>

  1. lang racket/gui

(require racket/date slideshow/pict)

(define (clock h m s [r 100])

 (define (draw-hand length angle 
                    #:width [width 1]
                    #:color [color "black"])
   (dc (λ (dc dx dy)
         (define old-pen (send dc get-pen))
         (send dc set-pen (new pen% [width width] [color color]))
         (send dc draw-line
               (+ dx r) (+ dy r)
               (+ dx r (* length (sin angle)))
               (+ dy r (* length (cos angle))))
         (send dc set-pen old-pen))
     (* 2 r) (* 2 r)))
 (cc-superimpose
  (for/fold ([pict (circle (* 2 r))])
            ([angle (in-range 0 (* 2 pi) (/ pi 6))]
             [hour (cons 12 (range 1 12))])
    (define angle* angle)
    (define r* (* r 0.8))
    (define txt (text (number->string hour) '(bold . "Helvetica")))
    (define x (- (* r* (sin angle*)) (/ (pict-width txt) 2)))
    (define y (+ (* r* (cos angle*)) (/ (pict-height txt) 2)))
    (pin-over pict (+ r x) (- r y) txt))
  (draw-hand (* r 0.7) (+ pi (* (modulo h 12) (- (/ pi 6))))
             #:width 3)
  (draw-hand (* r 0.5) (+ pi (* m (- (/ pi 30))))
             #:width 2)
  (draw-hand (* r 0.7) (+ pi (* s (- (/ pi 30))))
             #:color "red")
  (disk (* r 0.1))))

(define f (new frame% [label "Clock"] [width 300] [height 300]))

(define c

 (new canvas%
      [parent f]
      [paint-callback 
       (λ (c dc)
         (define date (current-date))
         (draw-pict (clock (date-hour date) 
                           (date-minute date)
                           (date-second date)
                           (/ (send c get-width) 2))
                    dc 0 0))]))

(define t

 (new timer% 
      [notify-callback (λ () (send c refresh-now))]
      [interval 1000]))

(send f show #t) </lang>

REXX

This REXX program draws a digital clock;   it shows the seconds if the terminal screen is
wide enough.

The   $T.REX   program does the heavy lifting of actually creating the blocked characters.

If using

  • PC/REXX
  • Personal REXX
  • R4
  • ROO

the color of the display can be specified.

The   $CLOCK.REX   REXX program makes use of $T REXX program which is used to display text and/or create big blocked characters.
The   $T.REX   REXX program is included here ──► $T.REX.
The help for the   $T.REX   REXX program is included here ──► $T.HEL.

The   $CLOCK.REX   REXX program makes use of   $ERR.REX   REXX program which is used to display error messages (via   $T.REX).
The   $ERR   REXX program is included here ──► $ERR.REX.

Some older REXXes don't have a   changestr   BIF, so one is included here ──► CHANGESTR.REX.

REXX programs not included are   $H.REX   which shows help and other documentation. <lang rexx>/**/trace o;parse arg !;if !all(arg()) then exit;if !cms then address signal on halt; signal on novalue; signal on syntax parse var ! ops; ops = space(ops) /*obtain command line options. */ @abc = 'abcdefghijklmnopqrstuvwxyz' /*alphabet str used by ABB/ABBN. */ blinkSecs = 1 creep = 1 tops = '.C=blue .BC=░ .BS=1 .BLOCK=12'

 do while ops\==;    parse var ops _1 2 1 _ . 1 y ops;    upper _
   select
   when _==','                    then nop
   when _1=='.' & pos("=",_)\==0  then tops=tops y
   when abbn('BLINKSECs')         then blinksecs=no()
   when abbn('CREEPs')            then creep=no()
   otherwise                      call er 55,y
   end   /*select*/
 end     /*while ops¬==*/

if \!pcrexx then blinkSecs=0 /*if ¬PC/REXX, turn off BLINKSECS*/ tops=space(tops) /*elide extraneous TOPS blanks.*/ parse value scrsize() with sd sw . /*get the term screens dimensions*/ oldTime=

          do until queued()\==0
          ct=time();  mn=substr(ct,4,2);  ss=right(ct,2);   i_=0;   p_=0
          call blinksec
          if ct==oldTime  then if !cms then 'CP SLEEP'; else call delay 1
          if creep  then do;            p_ =  3 + right(mn,1)
                         if sd>26  then p_ = p_ + left(mn,1)
                         if sd>33  then p_ = p_ + left(mn,1)
                         if sd>44  then p_ = p_ + left(mn,1) +right(mn,1)
                         end
          _p=-p_
          i_=2+left(ct,1);     ctt=left(ct,5);    if sw>108  then ctt=ct
          r=$t('.P='_p ".I="i_ tops ctt);         if r\==0   then leave
          oldTime=time()
          end   /*forever*/

exit /*stick a fork in it, we're done.*/ /*═════════════════════════════general 1-line subs════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════*/ !all:!!=!;!=space(!);upper !;call !fid;!nt=right(!var('OS'),2)=='NT';!cls=word('CLS VMFCLEAR CLRSCREEN',1+!cms+!tso*2);if arg(1)\==1 then return 0;if wordpos(!,'? ?SAMPLES ?AUTHOR ?FLOW')==0 then return 0;!call=']$H';call '$H' !fn !;!call=;return 1 !cal: if symbol('!CALL')\=="VAR" then !call=;return !call !env: !env='ENVIRONMENT';if !sys=='MSDOS'|!brexx|!r4|!roo then !env='SYSTEM';if !os2 then !env='OS2'!env;!ebcdic=1=='f0'x;return !fid: parse upper source !sys !fun !fid . 1 . . !fn !ft !fm .;call !sys;if !dos then do;_=lastpos('\',!fn);!fm=left(!fn,_);!fn=substr(!fn,_+1);parse var !fn !fn '.' !ft;end;return word(0 !fn !ft !fm,1+('0'arg(1))) !rex: parse upper version !ver !vernum !verdate .;!brexx='BY'==!vernum;!kexx='KEXX'==!ver;!pcrexx='REXX/PERSONAL'==!ver|'REXX/PC'==!ver;!r4='REXX-R4'==!ver;!regina='REXX-REGINA'==left(!ver,11);!roo='REXX-ROO'==!ver;call !env;return !sys: !cms=!sys=='CMS';!os2=!sys=='OS2';!tso=!sys=='TSO'|!sys=='MVS';!vse=!sys=='VSE';!dos=pos('DOS',!sys)\==0|pos('WIN',!sys)\==0|!sys=='CMD';call !rex;return !var: call !fid;if !kexx then return space(dosenv(arg(1)));return space(value(arg(1),,!env)) $t: !call=']$T';call "$T" arg(1);!call=;return result abb: arg abbu;parse arg abb;return abbrev(abbu,_,abbl(abb)) abbl: return verify(arg(1)'a',@abc,'M')-1 abbn: parse arg abbn;return abb(abbn)|abb('NO'abbn) blinksec: if \blinksecs then return;bsec=' ';ss2=right(ss,2);if sw<=80 then bsec=copies(' ',2+ss2) ss2;call scrwrite 1+right(mn,1),1,bsec,,,1;call cursor sd-right(mn,1),sw-length(bsec);return er: parse arg _1,_2;call '$ERR' "14"p(_1) p(word(_1,2) !fid(1)) _2;if _1<0 then return _1;exit result err: call er '-'arg(1),arg(2);return erx: call er '-'arg(1),arg(2);exit halt: call er .1 no: if arg(1)\== then call er 01,arg(2);return left(_,2)\=='NO' novalue:!sigl=sigl;call er 17,!fid(2) !fid(3) !sigl condition('D') sourceline(!sigl) p: return word(arg(1),1) syntax:!sigl=sigl;call er 13,!fid(2) !fid(3) !sigl !cal() condition('D') sourceline(!sigl)</lang> output

     ░░░░░░░░     ░░░░░░░░                     ░░       ░░░░░░░░░░                     ░░░     ░░░░░░░░░░
    ░░░░░░░░░░   ░░░░░░░░░░                   ░░░       ░░░░░░░░░░                    ░░░░     ░░░░░░░░░░
    ░░      ░░   ░░      ░░                  ░░░░       ░░                           ░░ ░░     ░░
    ░░      ░░           ░░       ░░           ░░       ░░               ░░         ░░  ░░     ░░
          ░░            ░░        ░░           ░░       ░░               ░░        ░░   ░░     ░░
         ░░           ░░░                      ░░       ░░░░░░░                   ░░░░░░░░░░   ░░░░░░░
       ░░             ░░░                      ░░       ░░░░░░░░                  ░░░░░░░░░░   ░░░░░░░░
     ░░                 ░░                     ░░              ░░                       ░░            ░░
    ░░                   ░░                    ░░               ░░                      ░░             ░░
    ░░      ░░   ░░      ░░       ░░           ░░       ░░      ░░       ░░             ░░     ░░      ░░
    ░░░░░░░░░░   ░░░░░░░░░░       ░░         ░░░░░░     ░░░░░░░░░░       ░░            ░░░░    ░░░░░░░░░░
    ░░░░░░░░░░    ░░░░░░░░                   ░░░░░░      ░░░░░░░░                      ░░░░     ░░░░░░░░

output   (when the terminal screen is less then 109 bytes)

      ░░░░░░░░     ░░░░░░░░                  ░░░░░░░░    ░░░░░░░░░░
     ░░░░░░░░░░   ░░░░░░░░░░                ░░░░░░░░░░   ░░░░░░░░░░
     ░░      ░░   ░░      ░░                ░░      ░░   ░░
     ░░      ░░           ░░       ░░       ░░      ░░   ░░
           ░░            ░░        ░░             ░░     ░░
          ░░           ░░░                       ░░      ░░░░░░░
        ░░             ░░░                     ░░        ░░░░░░░░
      ░░                 ░░                  ░░                 ░░
     ░░                   ░░                ░░                   ░░
     ░░      ░░   ░░      ░░       ░░       ░░      ░░   ░░      ░░
     ░░░░░░░░░░   ░░░░░░░░░░       ░░       ░░░░░░░░░░   ░░░░░░░░░░
     ░░░░░░░░░░    ░░░░░░░░                 ░░░░░░░░░░    ░░░░░░░░

Ruby

Library: Shoes
Sample display of Ruby solution

<lang ruby>Shoes.app(:width=>205, :height => 228, :title => "A Clock") do

 def draw_ray(width, start, stop, ratio)
   angle = Math::PI * 2 * ratio - Math::PI/2
   strokewidth width
   cos = Math::cos(angle)
   sin = Math::sin(angle)
   line 101+cos*start, 101+sin*start, 101+cos*stop, 101+sin*stop
 end
 def update
   t = Time.now
   @time.text = t.strftime("%H:%M:%S")
   h, m, s = (t.hour % 12).to_f, t.min.to_f, t.sec.to_f
   s += t.to_f - t.to_i  # add the fractional seconds
   @hands.clear do
     draw_ray(3, 0, 70, (h + m/60)/12)
     draw_ray(2, 0, 90, (m + s/60)/60)
     draw_ray(1, 0, 95, s/60)
   end
 end
 # a place for the text display
 @time = para(:align=>"center", :family => "monospace")
 # draw the clock face
 stack(:width=>203, :height=>203) do
   strokewidth 1
   fill gradient(deepskyblue, aqua)
   oval 1, 1, 200
   fill black
   oval 98, 98, 6
   # draw the minute indicators
   0.upto(59) {|m| draw_ray(1, (m % 5 == 0 ? 96 : 98), 100, m.to_f/60)}
 end.move(0,23)
 # the drawing area for the hands
 @hands = stack(:width=>203, :height=>203) {}.move(0,23)
 animate(5) {update}

end</lang>

Inspired by the PicoLisp solution, here's an implementation of the Berlin-Uhr clock.

Berlin-Uhr clock

<lang ruby>Shoes.app(:title => "Berlin-Uhr Clock", :width => 209, :height => 300) do

 background lightgrey
 Red = rgb(255, 20, 20)
 Yellow = rgb(173, 255, 47)
 Green = rgb(154, 205, 50)
 Gray = rgb(128, 128, 128)
 @time = para(:align => "center")
 stack do
   fill Gray
   stroke black
   strokewidth 2
   @seconds = oval 75, 3, 50
   @hrs_a  =  4.times.collect {|i| rect   51*i,  56, 48, 30, 4}
   @hrs_b  =  4.times.collect {|i| rect   51*i,  89, 48, 30, 4}
   @mins_a = 11.times.collect {|i| rect 2+18*i, 122, 15, 30, 4}
   @mins_b =  4.times.collect {|i| rect   51*i, 155, 48, 30, 4}
   # some decoration
   fill white
   stroke darkslategray
   rect -10, -30, 75, 70, 10
   rect 140, -30, 75, 70, 10
   rect -13, 192, 105, 100, 10
   rect 110, 192, 105, 100, 10
 end.move(3,20)
   
 animate(1) do
   now = Time.now
   @time.text = now.strftime("%H:%M:%S")
   @seconds.style(:fill => now.sec.even? ? Green : Gray)
   a, b = now.hour.divmod(5)
   4.times {|i| @hrs_a[i].style(:fill => i < a ? Red : Gray)}
   4.times {|i| @hrs_b[i].style(:fill => i < b ? Red : Gray)}
   a, b = now.min.divmod(5)
   11.times {|i| @mins_a[i].style(:fill => i < a ? (i%3==2 ? Red : Yellow) : Gray)}
   4.times  {|i| @mins_b[i].style(:fill => i < b ? Yellow : Gray)}
 end
 
 keypress do |key|
   case key
   when :control_q, "\x11" then exit
   end
 end

end</lang>

Run BASIC

Sample display of RB solution

<lang runbasic>' -------------------------------------------- ' clock. I got nothing but time ' --------------------------------------------- n = 12 ' num of points r = 95 ' radius pi = 22/7 alpha = pi * 2 / n dim points(n) graphic #g2, 200, 200 ' -------------------------------------- ' Draw the clock ' --------------------------------------

  1. g2 size(2) 'pen size
  2. g2 down()
  3. g2 font("arial", 20, "bold")
  4. g2 place(85,30)
  5. g2 "\12"
  6. g2 place(170,105)
  7. g2 "\3"
  8. g2 place(10,105)
  9. g2 "\9"
  10. g2 place(90,185)
  11. g2 "\6"

for i = 0 to n - 1 theta = alpha * i px = cos( theta ) * r py = sin( theta ) * r px = px + 100 py = py + 100 #g2 place(px,py) #g2 circle(2) next i

[shoTime] ' ------------------------- ' clear previous sec,min,hr ' ------------------------- r = 63 p = se

  1. g2 color("white")

gosub [h2Dot] r = 50 p = mi

  1. g2 color("white")

gosub [h2Dot] r = 30 ' radius p = hr * 5

  1. g2 color("white")

gosub [h2Dot]

' ------------------------- ' Show new time ' ------------------------- a$ = time$() hr = val(word$(a$,1,":")) mi = val(word$(a$,2,":")) se = val(word$(a$,3,":"))

' put time on the clock - gimme a hand

  1. g2 size(4)

' second hand n = 60 r = 63 p = se

  1. g2 color("blue")

gosub [h2Dot]

' minute hand r = 50 p = mi

  1. g2 color("green")

gosub [h2Dot]

' hour hand r = 30 ' radius p = hr * 5

  1. g2 color("red")

gosub [h2Dot]

render #g2 end

' a one liner [h2Dot] alpha = pi * 2 / n i = p - 15 theta = alpha * i px = cos( theta ) * r py = sin( theta ) * r px = px + 100 py = py + 100

  1. g2 place(px,py)
  2. g2 circle(2)
  3. g2 line(100,100,px,py)

RETURN</lang>

Tcl

Sample display of Tcl solution
Library: Tk

<lang tcl>package require Tcl 8.5 package require Tk

  1. GUI code

pack [canvas .c -width 200 -height 200] .c create oval 20 20 180 180 -width 10 -fill {} -outline grey70 .c create line 0 0 1 1 -tags hour -width 6 -cap round -fill black .c create line 0 0 1 1 -tags minute -width 4 -cap round -fill black .c create line 0 0 1 1 -tags second -width 2 -cap round -fill grey30 proc updateClock t {

   scan [clock format $t -format "%H %M %S"] "%d%d%d" h m s
   # On an analog clock, the hour and minute hands move gradually
   set m [expr {$m + $s/60.0}]
   set h [expr {($h % 12 + $m/60.0) * 5}]
   foreach tag {hour minute second} value [list $h $m $s] len {50 80 80} {

.c coords $tag 100 100 \ [expr {100 + $len*sin($value/30.0*3.14159)}] \ [expr {100 - $len*cos($value/30.0*3.14159)}]

   }

}

  1. Timer code, accurate to within a quarter second

set time 0 proc ticker {} {

   global time
   set t [clock seconds]
   after 250 ticker
   if {$t != $time} {

set time $t updateClock $t

   }

} ticker</lang> Note that though this code does poll the system timer approximately four times a second, this is a cheap operation; the GUI update (the relatively expensive part) only happens once a second. The amount of system processing power consumed by this code isn't noticeable on my system; it vanishes with respect to the other processing normally happening.