Saucer: your first hardware sprite in 20 lines
by The RetroMake desk 26 October 2026
The VIC-II can move eight 24 by 21 pixel objects over the screen without the processor touching the screen memory. This listing draws one, bounces it off the edges and changes the border as it goes.
A sprite is a small picture the video chip draws on top of the screen at a position you give it. The C64 has eight of them, each 24 pixels wide and 21 tall, and because the VIC-II composes them in hardware, moving one costs two POKEs rather than redrawing anything. This program draws a flying saucer and lets it drift.
How the listing works
Lines 200 to 260 hold the picture as 63 numbers: 21 rows of 3 bytes, each byte describing 8 pixels. Line 30 reads them into memory at 832, which is the cassette buffer, a 192-byte area the machine only uses while the tape drive is running. Sprite pictures must start at a multiple of 64, and 832 is 64 times 13, so line 40 writes 13 into address 2040, the pointer that tells the chip where sprite 0's picture lives.
Line 50 sets the sprite's colour register to 1, white. Line 60 turns the sprite on: register 53269 has one bit per sprite, and writing 1 enables the first. From there the program is a loop. Lines 80 to 110 write the X and Y position registers and reverse direction at the edges. The visible area for a sprite runs from X 24 to 343 and Y 50 to 229, but X values above 255 need a ninth bit in another register, so this listing turns around at 230 to keep things simple. Line 120 uses the saucer's position to recolour the border through register 53280, which is the cheapest special effect on the machine.
Things to try
Change the 1 in line 50 to any value from 0 to 15 for a different colour. POKE 53277,1 before the loop stretches the sprite to double width; POKE 53271,1 doubles its height. To add a second saucer, write 13 into 2041 and 2 into 53269 (3 enables both), and move it with 53250 and 53251. Replacing the DATA with your own picture is the real exercise: sketch on a 24 by 21 grid, convert each row of 8 cells to a number by adding 128, 64, 32, 16, 8, 4, 2 and 1 for the filled cells, and type the 63 results in order.
The listing
10 rem saucer: one sprite, bouncing
20 print "{clr}{lblu}saucer. run/stop ends it."
30 for i=0 to 62: read d: poke 832+i,d: next
40 poke 2040,13: rem sprite 0 uses block 13 (832)
50 poke 53287,1: rem white
60 poke 53269,1: rem enable sprite 0
70 x=24: y=100: dx=2: dy=1
80 poke 53248,x: poke 53249,y
90 x=x+dx: y=y+dy
100 if x<24 or x>230 then dx=-dx
110 if y<50 or y>229 then dy=-dy
120 poke 53280,(x/16) and 15
130 goto 80
200 data 0,0,0,0,0,0,0,126,0
210 data 1,255,128,3,255,192,7,255,224
220 data 15,255,240,31,255,248,63,255,252
230 data 127,255,254,255,255,255,127,255,254
240 data 63,255,252,0,255,0,0,126,0
250 data 0,60,0,0,24,0,0,0,0
260 data 0,0,0,0,0,0,0,0,0