# Lightning bolt effect in PICO-8

There are several methods to creating a lightning bolt effect, such as using fractals or midpoint displacement. The method I’ll be using is dividing a line into segments, offseting each vertex, and drawing several of these segmented lines on top of one another.

First, since we’re using PICO-8, we’ll need some helper functions for getting the length of a line, slope of a line as a unit vector, and the unit normal of a line.

``````--Helper functions
function magnitude(x1,y1,x2,y2)
return sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1))
end

function slope(x1,y1,x2,y2)
local l=magnitude(x1,y1,x2,y2)
local dx=(x2-x1)/l
local dy=(y2-y1)/l
return {dx,dy}
end

function normal(x1,y1,x2,y2)
local l=magnitude(x1,y1,x2,y2)
local dx=(x2-x1)/l
local dy=(y2-y1)/l
return {-dy,dx}
end``````

Next, in a new tab, we’ll be making a Lightning object, which represents a line with two endpoints and evenly-spaced vertices in between. In the constructor, which I’m calling `new()`, we’ll add arguments for the two endpoints, the number of segments this object will have, and the color. Then we add a points array, which will hold the vertices of the line’s segments, and then generate these vertices by calculating the slope of the line, then breaking up the line into equal-length segments, and adding each vertex to the points array.

``````lightning={}
function lightning:new(x1,y1,x2,y2,n,c)
local o={}
setmetatable(o, self)
self.__index=self

o.x1=x1
o.x2=x2
o.y1=y1
o.y2=y2
o.color=c
o.points={}

--create the line segments
--make sure the minimum is 1
o.num_segments=flr(max(1,n))
local len=magnitude(x1,y1,x2,y2)/o.num_segments
local p1={x1,y1}
local p2={x2,y2}
local vec=slope(x1,y1,x2,y2)
for i=1,o.num_segments do
local p={p1[1],p1[2]}
p[1]+=i*len*vec[1]
p[2]+=i*len*vec[2]
end

return o
end``````

Now we’ll add a function to “generate” the lightning bolt, which really just means offsetting each vertex by adding the unit normal of the line to each one, scaled to a random amount. Note that each time this method is called, we also need to reset the positions of each vertex because otherwise, they’ll keep moving farther and farther away.

``````function lightning:generate()
local x1=self.x1
local y1=self.y1
local x2=self.x2
local y2=self.y2
local nor=normal(x1,y1,x2,y2)
local vec=slope(x1,y1,x2,y2)
local len=magnitude(x1,y1,x2,y2)/self.num_segments
for i=1,#self.points do
local p=self.points[i]
--First reset back to normal
p[1]=x1+(i-1)*len*vec[1]
p[2]=y1+(i-1)*len*vec[2]
--Then offset the point by adding the normal vector
local off=flr(rnd(12))-6
p[1]+=off*nor[1]
p[2]+=off*nor[2]
end
end``````

The offset applied to each vertex is hard-coded to be between -6 and 5, but you can change this to whatever you want, or even make it dynamic. Also note that each point, including the two endpoints, are offset. If you want an effect where the endpoints stay in place, you can change `for i=1,#self.points do` to `for i=2,#self.points-1 do` instead, which will ignore the first and last points.

Finally, we’ll add a `draw()` function to the Lightning object that will be called in the main `_draw()` function. In the draw function, we call PICO-8’s `line()` function by passing each pair of vertices in order.

``````function lightning:draw()
for i=1,#self.points-1 do
local p1=self.points[i]
local p2=self.points[i+1]
line(p1[1],p1[2],p2[1],p2[2],self.color)
end
end``````

Now we can move on to the main loop. First we’ll create an array to hold Lightning objects, and in the `_init()` function we’ll create three Lightning objects and add them to this array.

``````lightning_bolt={}
function _init()
bolt=lightning:new(16,16,112,112,7,10)
bolt2=lightning:new(16,16,112,112,7,10)
bolt3=lightning:new(16,16,112,112,7,10)
end``````

Next, in the `_draw()` function, we loop through each Lightning object in the array and call their `draw()` function.

``````function _draw()
cls()
for v in all(lightning_bolt) do
v:draw()
end
end``````

Finally, in the `_update()` function, for demonstration purposes, we’ll check if button 4 (Z/C/N) is being pressed, and if so, call `generate()` on all the Lightning objects in the array. That way, if you hold down the button, each lightning bolt will regenerate its vertices’ offsets, creating a lightning bolt effect.

``````function _update()
if btn(4) then
for v in all(lightning_bolt) do
v:generate()
end
end
end``````

After implementing the above Lightning object, you can freely reuse it in any other projects too. You can add an `update()` function to it with custom behavior, or make `generate()` run constantly every set interval, or even manipulate the endpoints to make the lightning move around.