Alan A. A. Donovan and Brian W. Kernighan, The Go Programming Language. Addison-Wesley, 2016, Chapter 1, Tutorial

Exercise 1.12

Modify the Lissajous server to read parameter values from the URL. For example, you might arrange it so that a URL like http://localhost:8000/?cycles=20 sets the number of cycles to 20 instead of the default 5. Use the strconv.Atoi function to convert the string parameter into an integer. You can see its documentation with go doc strconv.Atoi.

My (lazy) solution

Incorporates the changes I made for Exercise 1.6.

Assumes that the query segment of the URL containes only one item and that that item is delay.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
package main

import (
    "image"
    "image/color"
    "image/gif"
    "io"
    "log"
    "math"
    "math/rand"
    "net/http"
    "net/url"
	"regexp"
	"strconv"
)

const (
	blackIndex = 0 // first color in palette
	whiteIndex = 1 // next color in palette
	greenIndex = 2 // for Exercise 1.5
	gray1Index = 3 // for Exercise 1.6
	gray2Index = 4
	gray3Index = 5
	gray4Index = 6
)

var (
	green   = color.RGBA{0, 255, 0, 255}
	gray1   = color.RGBA{211, 211, 211, 255} // light gray
	gray2   = color.RGBA{169, 169, 169, 255} // dark gray
	gray3   = color.RGBA{105, 105, 106, 255} // dim gray
	gray4   = color.RGBA{112, 128, 144, 255} // slate gray
	palette = []color.Color{color.Black, color.White, green,
		gray1, gray2, gray3, gray4}
)

func lissajous(out io.Writer, delay int) {
	const (
		cycles  = 5     // number of complex x oscillator revolutions
		res     = 0.001 // angular resolution
		size    = 100   // image canvas covers [-size..+size]
		nframes = 64    // number of animation frames
		// delay   = 8     // delay between frames in 10ms units
	)

	freq := rand.Float64() * 3.0 // relative frequency of y oscillator
	anim := gif.GIF{LoopCount: nframes}
	phase := 0.0 // phase difference

	// Set line color index value. The third argument of 
	// `image.Palleted.SetColorIndex()` must be of type `uint8`,
	// so cast it here.
	colorChoice := uint8(3)

	// Create `nframes` images, each representing a frame of the
	// animation, storing them in the struct `anim`, interspersed
	// with delays of the value of `delay` each.
	for i := 0; i < nframes; i++ {
		rect := image.Rect(0, 0, 2*size+1, 2*size+1)
		img := image.NewPaletted(rect, palette)

		// Cycle the line color choice.
		colorChoice += 1
		if colorChoice == 7 {
			colorChoice = uint8(3)
		}

		// Run the two oscillators.
		for t := 0.0; t < cycles*2*math.Pi; t += res {

			// x oscillator is a sine function.
			x := math.Sin(t)

			// Frequency of y oscillator incorporates a random
			// value and the increasing value of `phase`.
			y := math.Sin(t*freq + phase)

			// Set the pixel x,y to a randomly chosen color.
			img.SetColorIndex(
				size+int(x*size+0.5),
				size+int(y*size+0.5),
				colorChoice)
		}
		phase += 0.1
		anim.Delay = append(anim.Delay, delay)
		anim.Image = append(anim.Image, img)
	}

	// Encode the sequence of frames and delays in `anim` and
	// write to output.
	gif.EncodeAll(out, &anim)
}

func handler(w http.ResponseWriter, r *http.Request) {
	// Parse the URL, using `net/url`.
	u, err := url.Parse(r.URL.String())
	if err != nil {
		panic(err)
	}
	
	// Assume the query segment of the URL contains only one item
	// and that that item is `delay`.
	re, _ := regexp.Compile("[0-9]+")
	delay, _ := strconv.Atoi(re.FindString(u.RawQuery))
	lissajous(w, delay)
}

func server() {
    http.HandleFunc("/", handler)
    log.Fatal(http.ListenAndServe("localhost:8000", nil))
}

func main() {
	server()
}

Execute this file

$ codedown go < 2022-06-23-lissajous.md | grep . > /tmp/tmp.go && go run /tmp/tmp.go &
$ open http://localhost:8000/?delay=5