Just draw different colors to the background.
1local color=0 -- colors go from 0 to 152delay(500) -- every draw call will take 0.5 seconds34function draw()5 clear(color) -- clear the screen with current color6 color=color+1 -- increment color every7 if color==16 then stop() end -- stop after last color was reached8end
Example with all basic shapes.
1clear(7) -- white background2line(16,12,48,36,0) -- line from (16,12) to (48,36) in black3rect(60,12,24,24,9) -- rectangle outline at (60,12) with width and height of 24 in orange4rectfill(96,12,48,24,12) -- filled rectangle at (96,12) with width 48 and height 24 in blue5circle(80,80,24,8) -- circle outline at the center with radius 24 in red6circlefill(80,80,18,2) -- filled circle at center with radius 18 in dark purple7point(80,80,7) -- white point at center8arc(30,80,18,0,270,12) -- arc outline at (30,80) with radius 18 going from 0 to 270 degress in blue9arcfill(130,80,18,0,180,14) -- filled arc at (130,80) with radius 18 going from 0 to 180 degress in pink10text('this is a text',16,130,0) -- text at (16,130) in black11icon('a',118,124,9) -- a strange icon at (118,124) in orange1213--- note: only 1 frame is created, as there is no draw() loop
Play around with text.
1local angle=0 -- rotation angle2local sf=1 -- scale factor3local sf_dx=.02 -- change of scale factor4local runs=0 -- number of runs56function draw()7 scale(sf,sf) -- scale the screen by scale factor8 clear(1)9 angle=angle+1 -- increment angle by 1 degree10 translate(80/sf,80/sf) rotate(angle) -- rotate around center (scaled)11 text("AWESOME!",-12*sf+1,-4*sf-1,9)12 text("AWESOME!",-12*sf,-4*sf,7) -- white text and orange shadow, relative to center13 transform(1,0,0,1,0,0,true) -- reset all scaling, rotating and moving14 sf=sf+sf_dx -- change scale factor each frame15 if sf>2 then sf_dx=-0.02 runs=runs+1 end -- if scale factor is >2 decrease from now on16 if sf<1 then17 if runs<3 then sf_dx=0.02 else sf_dx=0 end -- increase again, if factor <1, but only 3 times18 if angle>360 then stop() end -- stop after full rotation19 end20end
Draw icons to screen.
1-- check out https://fontmeme.com/fonts/pixel-icons-compilation-font/2-- for list of characters/icons.34clear(15)5color(1) -- change draw color to dark blue (unless other color is used)6icon('abcdefg',6,24)7icon('hijklmn',6,48)8icon('opqrstu',6,72)9icon('vwxyz',6,96)
It's possible to draw sprites to the screen by defining strings with pixel data.
1-- Define heart pixel data2heart={}34heart[1] = [[5 82 826878288827888888828888888829 88888210 888211 8212]]1314heart[2] = [[15 82 8216887288821787888882188888888219 88888220 888221 8222]]2324heart[3] = [[25 82 8226888288822788788882288888888229 88888230 888231 8232]]3334-- setup35delay(250)36scale(8,8)37clear(12)38line(8,15,12,15,5)3940-- draw the sprites41for i=1,len(heart) do42 sprite(heart[i],8,6,6)43 render()44end
Instead of using pixel coordinates you can also position (multiline-)text with some convenience functions.
1linedash(2,2)2line(80,0,80,160,6)3line(0,80,160,80,6)45-- Position text conveniently6text('left\ntop','left','top',7)7text('center\ntop','center','top',7)8text('right\ntop','right','top',7)910textb('left\ncenter','left','center',7,9)11textb('center\ncenter','center','center',7,8)12textb('right\ncenter','right','center',7,9)1314text('left\nbottom','left','bottom',10)15text('center\nbottom','center','bottom',10)16text('right\nbottom','right','bottom',10)
Draw a triangle by using path() functions.
1clear(12)2path_begin() -- begin path3 path_move(32,128) -- move pointer to bottom left4 path_line(80,32) -- make line to top middle5 path_line(128,128) -- make line to bottom right6path_close() -- close path7path_fill(14) -- fill pink8path_stroke(7) -- draw white outline
Draw transparent shapes by using the alpha.
1clear(15)2rectfill(24,24,80,80,12) -- draw a blue rectangle3alpha(0.5) -- set transparency to 50%4rectfill(56,56,80,80,8) -- draw a red rectangle (using transparency now)
Use styling effects for all line draw operations.
1local offset=0 -- offset parameter23function draw()4 offset=offset-.55 clear(2)6 linewidth(2) -- set line width to 2 pixels7 linedash(4,4,offset) -- use linedash effect: 4px are drawn, 4px are not drawn, segments are offsetted each frame8 rect(16,62,128,32,7) -- rectangle outline will use the effect9 text("YOU WIN!","center","center")10 if offset< -6 then stop() end11end
Clip the screen area to allow drawing only on certain areas.
1local x=02clip(0,0,80,160) -- clip the draw area to the left half of the screen34function draw()5 clear(6)6 x=x+17 rectfill(x,72,16,16,5) -- draw a rectangle, will disappear in the right half of the screen8 if x>144 then stop() end9end
Need different colors? Just change them.
1-- Change first 5 colors2palette(0,'cadca0')3palette(1,'9bbb0e')4palette(2,'8aac0f')5palette(3,'30622f')6palette(4,'0f380e')78-- All draw operations will now use these colors9rectfill(80,80,80,80,1)10rectfill(0,80,80,80,2)11rectfill(80,0,80,80,3)12rectfill(0,0,80,80,4)13circlefill(80,80,40,0)14circle(80,80,40,2)
Using paths to draw complex shapes.
1local w=12local dw=.23translate(7, 7)45function draw()6 w=w+dw7 if w>6 then dw=-.2 end8 if w<1 then dw=.2 end9 linewidth(w)10 clear(12)11 -- heart12 path_begin()13 path_move(75, 40)14 path_bezier(75, 37, 70, 25, 50, 25)15 path_bezier(20, 25, 20, 62.5, 20, 62.5)16 path_bezier(20, 80, 40, 102, 75, 120)17 path_bezier(110, 102, 130, 80, 130, 62.5)18 path_bezier(130, 62.5, 130, 25, 100, 25)19 path_bezier(85, 25, 75, 37, 75, 40)20 path_fill(8)21 path_stroke(7)22 if frames()>50 then stop() end23end
A non-looping example. Also pixel colors are read to detect if square is on the ground.
1loop(-1)2local y = 03local dy = 145function draw()6 clear(10)7 line(0, 80, 160, 80, 0)8 y = y + dy9 if pixel(80, y + 8) == 0 then dy = 0 text("Boom!", 12, 12, 0) stop() end10 rectfill(76, y, 8, 8, 8)11end
In this example the delay() command is heavily used to control the speed of individual frames.
1-- Helper functions2-- Show rolling text, controlled by delay() and render()3function text_roll(label, time)4 delay(time)5 for i=1,string.len(label) do6 content=string.sub(label, 1, i)7 clear(12)8 textb(content,'center','center',7,0)9 icon('t',128,128,7)10 render()11 end12end1314-- Show text, controlled by delay() and render()15function text_punch(label, time)16 delay(time)17 clear(12)18 textb(label,'center','center',7,0)19 icon('t',128,128,7)20 render()21end2223-- Wait = just render() a new frame with a certain delay24function wait(time)25 delay(time)26 render()27end2829-- Animation30text_roll("Ooh, baby, do you know", 110)31wait(80)32text_punch("Ooh, baby, do you know\nwhat",400)33text_punch("Ooh, baby, do you know\nwhat it's",400)34text_punch("Ooh, baby, do you know\nwhat it's worth",400)3536wait(800)3738text_roll("Ooh, heaven is a",115)39wait(80)40text_punch("Ooh, heaven is a\nplace",400)41text_punch("Ooh, heaven is a\nplace on",400)42text_punch("Ooh, heaven is a\nplace on earth",400)43wait(600)
An advanced example using cosinus to draw a skipping rope. The "fellas" are drawn as ascii art. (^_^)
1guy="웃"2j=034function draw()5 clear(12)6 k=j7 j=math.cos(frames()*.15)*128 y=j+449 rectfill(16,51,127,92,1)10 rectfill(50,45,60,99,12)11 rectfill(70,64,20,80,7)12 rectfill(0,142,180,28,9)13 c=614 if k>=j then text(guy,73,y,8) c=10 end15 for i=48,112 do16 point(i,math.cos(math.rad (i*2.2))*j+44,c)17 end18 if k<j then text(guy,73,y,8) end19 if y<44 then text("hop",73,y-16,7) end20 o=j*-.121 text(guy,38+o,42,1)22 text(guy,111-o,42,1)23 if frames()>40 then stop() end24end
An example how #genart can be created using gifboy.
1rand=math.random2local count=03delay(500)45function draw()6 -- generate points7 q={} -- points to draw8 for x=0,30 do9 for y=0,30 do10 p={} -- temporary table11 p.u=x/1812 p.v=y/1813 p.r=math.floor(rand(2))*rand(4)14 p.c=rand(6)+715 if rand()>.5 then table.insert(q,p) end16 end17 end1819 -- draw the work20 clear(7)21 count=count+122 for i=1,len(q),1 do23 p=q[i]24 x=spread(12,96,p.u)25 y=spread(12,96,p.v)26 circlefill(x,y,p.r,p.c)27 end28 if count>9 then stop() end29end3031-- spread points helper32function spread(x,y,pos)33 return y*pos+x*(1-pos)34end
Achieving a water reflection effect by using pixelcpy().
1guy="웃"2j=034function draw()5 clear(10)6 draw_scene()7 --mirror bottom 50%8 for i=80,1,-1 do9 pixelcpy((160-i)*160,i*160,160)10 end11 -- blue overlay12 alpha(.5)13 rectfill(0,81,160,80,12)14 alpha(1)15 line(0,81,160,81,0)16 if frames()>40 then stop() end17end1819function draw_scene()20 k=j21 j=math.cos(frames()*.15)*1222 y=j+4423 rectfill(16,51,127,92,1)24 rectfill(50,45,60,99,10)25 rectfill(70,64,20,80,7)26 rectfill(0,142,180,28,9)27 c=628 if k>=j then text(guy,73,y,8) c=8 end29 for i=48,112 do30 point(i,math.cos(math.rad (i*2.2))*j+44,c)31 end32 if k<j then text(guy,73,y,8) end33 if y<44 then text("hop",73,y-16,7) end34 o=j*-.135 text(guy,38+o,42,1)36 text(guy,111-o,42,1)37end
You can use path_clip() to set an existing path as the clipping area. This can be used to achieve some complex effects while drawing.
1-- draw all 16 colors to the screen2-- starting color is defined by c3function draw_colors(c)4 c=c-15 for y=0,160,10 do6 c=c+17 rectfill(0,y,160,10,c)8 end9end1011-- set a star-shaped clipping area12function clip_star(cx, cy, spikes, outer_radius, inner_radius)13 rot = math.pi / 2 * 314 x = cx15 y = cy16 step = math.pi / spikes17 path_begin()18 path_move(cx, cy - outer_radius)19 for i=0,spikes-1 do20 x=cx+math.cos(rot)*outer_radius21 y=cy+math.sin(rot)*outer_radius22 path_line(x, y)23 rot=rot+step24 x=cx+math.cos(rot) * inner_radius25 y=cy+math.sin(rot) * inner_radius26 path_line(x, y)27 rot=rot+step28 end29 path_line(cx, cy - outer_radius)30 path_close()31 path_clip()32end3334col=-1 -- starting color for draw_colors35size=-16 -- additional size for star36dsize=1 -- direction of size effect37function draw()38 if frames()%8==0 then col=col+1 end -- change starting_color every 8 frames39 if frames()>64 then dsize=-1 end -- grow star in size ...40 if size<-16 then stop() end -- ... then shrink back to original value41 size=size+dsize -- change size for start42 clear(2) -- background color43 clip_star(80,80,5,32+size,16+size) -- apply star clip44 draw_colors(col) -- draw colors (only in clipped area)45end
A starfield example, using lua tables and simple 3D projections.
1flr=math.floor2rnd=math.random3stars={}4stars.points={}5stars.count=1306stars.distance=18078function init()9 local range=250010 for i=1,stars.count do11 xp=flr(range-rnd(range*2))12 yp=flr(range-rnd(range*2))13 zp=rnd(stars.distance)14 table.insert(stars.points,{x=xp,y=yp,z=zp})15 end16end1718init()1920function draw()21 clear(0)22 update()23 if frames()>300 then stop() end24end2526function update()27 for i=1,len(stars.points) do28 stars.points[i].z=stars.points[i].z-129 if stars.points[i].z<=0 then30 stars.points[i].z=stars.distance31 end32 end3334 for i=1,len(stars.points) do35 local cz=stars.points[i].z36 local cx=stars.points[i].x/cz37 local cy=stars.points[i].y/cz38 if cx< -80 or cx>80 then39 stars.points[i].z=stars.distance40 end41 if cy< -80 or cy>80 then42 stars.points[i].z=stars.distance43 end44 local cols={7,6,5}45 local ci=1+flr(cz/stars.distance * len(cols))46 if ci>3 then ci=3 end47 rectfill(80+cx,80+cy,2,2,cols[ci])48 end49end
A simple 3D cube wireframe, using screen projections.
1pts={}2for z=-1,1,2 do3 for y=-1,1,2 do4 for x=-1,1,2 do5 table.insert(pts,{x,y,z})6 end7 end8end9lines={{1,2},{2,4},{1,3},{3,4},{5,6},{5,7},{6,8},{7,8},{1,5},{2,6},{3,7},{4,8}}10cam={0,0,-3}11fov=8012linewidth(6)13linedash(2,2)1415function draw_cube()16 for k,v in pairs(lines) do17 draw_line(pts[v[1]],pts[v[2]],10)18 end19end2021function draw_line(p1,p2,c)22 x1 = (p1[1]-cam[1])*fov/(p1[3]-cam[3]) + 8023 y1 = -(p1[2]-cam[2])*fov/(p1[3]-cam[3]) + 8024 x2 = (p2[1]-cam[1])*fov/(p2[3]-cam[3]) + 8025 y2 = -(p2[2]-cam[2])*fov/(p2[3]-cam[3]) + 8026 line(x1,y1,x2,y2,c)27end2829function rotate_shape(shape,a,r)30 new_shape = {}31 for k,v in pairs(pts) do32 table.insert(new_shape, rotate_point(v,a,r)) -- Rotate the point and add it to the new shape33 end34 return new_shape35end3637function rotate_point(p,a,r)38 if a==1 then39 x,y,z = 3,2,140 elseif a==2 then41 x,y,z = 1,3,242 elseif a==3 then43 x,y,z = 1,2,344 end45 _x = math.cos(r)*(p[x]) - math.sin(r) * (p[y])46 _y = math.sin(r)*(p[x]) + math.cos(r) * (p[y])47 np = {}48 np[x] = _x49 np[y] = _y50 np[z] = p[z]51 return np52end5354function draw()55 axis=156 if frames()<60 then axis=2 end57 if frames()>120 then axis=3 end58 if frames()>180 then stop() end59 clear()60 pts = rotate_shape(pts,axis,.0532)61 draw_cube()62end
No graphics engine is complete without showing a DOOM-like demo. Here we go: a raytracer demo similar to the graphical style of Wolfenstein 3D.
1--gifboy-wolfenstein2--based on lodev's and yonaba's raycasting tutorial34map={5width=10,6height=10,7{1,0,0,0,1,1,1,1,1,1},8{1,0,0,0,0,0,0,0,0,1},9{1,0,0,0,0,0,0,0,0,1},10{1,0,0,0,0,0,0,0,0,1},11{1,0,0,0,0,0,0,0,0,1},12{1,0,0,0,1,0,0,0,0,1},13{1,1,0,0,1,1,0,0,0,1},14{1,0,0,0,1,0,0,0,0,1},15{1,1,0,0,1,1,0,0,0,1},16{1,1,1,1,1,1,1,2,2,1}17}1819posx=120posy=321dirx=122diry=023planex=024planey=0.6625rotation=math.pi/226direction=127screenx=16028screeny=16029colorRoof=630colorSky=531colorStripes=103233--3D loop34delay(150)35for i=1,24 do36clear()37x=038for x=0,screenx do39camx=2*x/screenx-140rayposx=posx41rayposy=posy42raydirx=dirx+planex*camx43raydiry=diry+planey*camx44mapx=rayposx45mapy=rayposy46sidedistx=047sidedisty=048deltadistx=math.sqrt(1+(raydiry*raydiry)/(raydirx*raydirx))49deltadisty=math.sqrt(1+(raydirx*raydirx)/(raydiry*raydiry))50perp=051stepx=052stepy=053hit=054side=055if raydirx<0 then56stepx=-157sidedistx=(rayposx-mapx)*deltadistx58else stepx=159sidedistx=(mapx+1-rayposx)*deltadistx60end61if raydiry<0 then62stepy=-163sidedisty=(rayposy-mapy)*deltadisty64else stepy=165sidedisty=(mapy+1-rayposy)*deltadisty66end67while hit==0 do68if sidedistx<sidedisty then69sidedistx=sidedistx+deltadistx70mapx=mapx+stepx71side=072else73sidedisty=sidedisty+deltadisty74mapy=mapy+stepy75side=176end77if map[math.floor(mapx)][math.floor(mapy)]>0 then hit=1 end78end79if side==0 then80perp=math.abs((mapx-rayposx+(1-stepx)/2)/raydirx)81else82perp=math.abs((mapy-rayposy+(1-stepy)/2)/raydiry)83end84lineheight=math.abs(screeny/perp)85draw=-lineheight/2+screeny/286if draw<0 then draw=0 end87drawE=lineheight/2+screeny/288if drawE>=screenx then drawE=screenx-1 end89a=6590b=5891c=6392if side==1 then93a=a-3094b=b-3095c=c-3096end97if map[math.floor(mapx)][math.floor(mapy)]==2 then a,b,c=255,199,101 end98palette(14,a,b,c)99bandUp=screeny/2+drawE100bandDown=screeny/2+draw101rectfill(x,draw,1,drawE,14);102rectfill(x,drawE,1,272,colorRoof);103rectfill(x,0,1,draw,colorSky);104rectfill(x,bandUp/2,1,1,colorStripes)105rectfill(x,bandDown/2,1,1,colorStripes)106rectfill(x,screeny/2,1,1,colorStripes)107end108render()109if frames()<5 then posx=posx+1 end110if frames()>=5 and frames()<11 then posy=posy+1 end111if frames()>=15 and frames()<20 then posx=posx+1 end112end113114-- scene end115delay(2000)116clear()117scale(2,2)118textb('Level Won','center','center',7,1)119render()120stop()
Do you want to deepen your understanding of the available gifboy commands?
Check out the Documentation.