Light is a fascinating thing in our universe. We perceive it as color, warmth and vision. Yet it does things one may not expect it to do. One of the experiments that called for a better physical model of light was the double slit experiment. In this experiment, a laser is shone through two closely adjacent slits and projected on the screen behind. Using old physical models, one would expect to see one or maybe two specs of light on the screen, when in reality there appear alternating dark and bright spots.
To explain why this seemingly strange phenomenon is occurring, one can either see light as photons and comprehend that a photon presumably follows every possible path there is in the entire universe and then — through it being observed — randomly chooses one path and thus creates stripes (according to the theory of quantum mechanics) or one can see light as simply being a wave.
For more information on the double-slit experiment, I refer to this Wikipedia entry.
The animation shown below describes light as a wave. The green vectors represent the light wave’s phase at the points on the light beam, the yellow vector represents the addition of both of the slit’s light beam’s phase when hitting the screen and the red dots at the screen represent the light’s brightness at that point (defined by the yellow vector’s length).
To create the animation, Python and a Python module called PIL were used to create single frames which were then stitched together by ImageMagick to create an animated gif.
# Jonathan Frech, 18th of January 2017 # edited 19th of January 2017 # edited 22nd of January 2017 # edited 27th of January 2017
# import from PIL import Image, ImageDraw import sys, copy, os, math """ INIT """ # frame steps steps = 5 # image size w, h = 1080, 720 size = (w, h) # both slits pos1 = (50, h/2-200) pos2 = (50, h/2+200) # length of arrows (green lines) arrowlen = 40 # how does the wave length relate to the beam wavesteps = .01 # how fast phi changes (smaller is faster) phifactor = 10. # how fast the arrows spin (bigger is faster, just for visual show) wavevisualspeed = 0 # how many points on the beam should be skipped whilst drawing drawskip = 100 # range in which the destination moves yrange = range(0, h+1, steps) # all light points light =  # directory for saving the frames path = os.path.abspath(os.path.dirname(__file__)) + "/p/" """ FUNCTIONS """ # draw functions def circle(draw, p, r, fill): draw.ellipse((p-r, p-r, p+r, p+r), fill = fill) def line(draw, p1, p2, fill, width = 1): draw.line(p1+p2, fill = fill, width = width) # vector functions def veclen(vec): return math.sqrt(vec**2+vec**2) def vecmul(vec, n): return (vec*n, vec*n) def vecnorm(vec): return vecmul(vec, 1./veclen(vec)) def vecadd(vec1, vec2): return (vec1+vec2, vec1+vec2) def newvector(p1, p2): return (p1-p2, p1-p2) # get the point on a circle def circlepos(p, r, phi): return (p+r*math.cos(phi), p+r*math.sin(phi)) # fill an int with zeros def fill(n, l): return ("0"*(l-len(str(n))))+str(n) # get beam's destination def getdest(y): return (w-100, y) # get phi shift (only for show, makes the green arrows turn) def getphishift(y): return y*2*math.pi/h*wavevisualspeed """ FRAME """ # draw a frame def frame(y, filename): # determine destination point (moves up and down) dest = getdest(y) # colors white = (255, 255, 255) yellow = (255, 255, 0) green = (0, 150, 0) # save the image, using pillow (PIL) img = Image.new("RGB", size) pix = img.load() draw = ImageDraw.Draw(img) # draw points circle(draw, pos1, 5, white) circle(draw, pos2, 5, white) circle(draw, dest, 5, white) # center line line(draw, (0, h/2), (w, h/2), (50, 50, 50)) # draw beam line(draw, pos1, dest, white) line(draw, pos2, dest, white) # calculate vectors between p and dest vec1 = newvector(dest, pos1) vec2 = newvector(dest, pos2) # draw both beams for vec, pos in ((vec1, pos1), (vec2, pos2)): # number of vectors drawn n = int(veclen(vec)/wavesteps) # draw vectors for _ in range(n+1): if _ % drawskip == 0: v = vecmul(vec, 1./veclen(vec)*wavesteps*_) p = vecadd(pos, v) phi = veclen(v)/phifactor+getphishift(y) cv = circlepos((0, 0), arrowlen, phi) line(draw, p, vecadd(p, cv), green) # circle vector at the destination cv1 = circlepos((0, 0), arrowlen, veclen(vec1)/phifactor) cv2 = circlepos((0, 0), arrowlen, veclen(vec2)/phifactor) cv = vecadd(cv1, cv2) line(draw, dest, vecadd(dest, cv), yellow, 3) # new light point, draw all light points light.append((dest, int(255./maxcvlength*veclen(cv)))) for p, c in light: circle(draw, p, 3, (c, 0, 0)) # save image try: img.save(path + "%s.png" % filename, "PNG") except: pass """ MAIN """ # main def main(): global maxcvlength # determine maximum end vector length (to calculate color) maxcvlength = 0 for y in yrange: dest = getdest(y) vec1 = newvector(dest, pos1) vec2 = newvector(dest, pos2) cv1 = circlepos((0, 0), arrowlen, veclen(vec1)/phifactor+getphishift(y)) cv2 = circlepos((0, 0), arrowlen, veclen(vec2)/phifactor+getphishift(y)) cv = vecadd(cv1, cv2) cvlength = veclen(cv) maxcvlength = max(maxcvlength, cvlength) # create directory if it does not already exists, if it does, delete it and create a new one try: if not os.path.exists(path): print "Creating directory '%s'..." % path os.mkdir(path) else: print "Cleaning up directory '%s'..." % path i = 0 for _ in os.listdir(path): if os.path.isfile(path+_): os.remove(path+_) i += 1 print "Removed %d file%s." % (i, ["s", ""][i==1]) except: print "Failed to sort out directors '%s'." % path # calculate all frames print "Calculating frames..." i = 0 for y in yrange: frame(y, fill(i, 3)) i += 1 for y in yrange[::-1]: frame(y, fill(i, 3)) i += 1 # stitch frames to one animated gif try: print "Creating gif..." os.system("convert -delay 1 p/*.png p/out.gif") except: print "Failed to create gif." # run main()