I’ve been making some generative art pieces lately and publishing them on my other Instagram account and I wanted to make some animations. Result first:
So far I had only been using the excellent gg (Go Graphics) package for some static art. But how do you go from some static images to a video?
Pretty easy apparently and you can follow along with the simple project I setup as a playground.
Of course, make sure ffmpeg
is installed on your OS and also install the gg
package with:
go get -u github.com/fogleman/gg
Once it’s done, drop the following code in a file, say main.go
. By the way, this code and my other generative art code is made available on GitHub.
package main
import (
"fmt"
"image/color"
"math"
"math/rand"
"github.com/fogleman/gg"
)
type ego struct {
X, Y, Radius, Angle, Speed float64
}
func (v *ego) init(maxRadius float64, speed float64) {
v.Radius = float64(rand.Int31n(int32(maxRadius)))
v.Angle = float64(rand.Int31n(180))
v.Speed = speed
}
func (v *ego) travel() {
// Move the angle forward
v.Angle += v.Speed
// Adjust the X, Y coordinates
sin, cos := math.Sincos(gg.Radians(v.Angle))
v.X = v.Radius * cos
v.Y = v.Radius * sin
}
func main() {
// config
const (
s = 2000.0 // Size of final image
maxRadius = (s - (s * 0.15)) / 2 // Max radius of the circle around which egos travel
)
// How many egos will be travelling?
egos := [30]ego{}
// Initialize all of them
for i := range egos {
egos[i] = ego{}
egos[i].init(maxRadius, 0.5+rand.Float64()*6.0)
}
// Rotate all the egos until we complete a circle,
// and then, for each rotation...
for i := 0; i < 360; i++ {
// Start a drawing context
dc := gg.NewContext(int(s), int(s))
// Set a background color
dc.SetColor(color.RGBA{0, 0, 0, 255})
dc.Clear()
// Draw all the egos
for n := range egos {
ego := &egos[n]
ego.travel()
// Draw the orbit
dc.SetColor(color.NRGBA{255, 255, 255, 60})
dc.DrawCircle(s/2, s/2, ego.Radius)
dc.SetLineWidth(3)
dc.Stroke()
// Draw the position
dc.SetColor(color.RGBA{255, 0, 0, 255})
dc.DrawCircle(ego.X+s/2, ego.Y+s/2, 12)
dc.Fill()
}
// Save the output
dc.SavePNG(fmt.Sprintf("i-%d.png", i))
}
}
Run the code with
go run main.go
After a short while and letting the command run it’s course, you likely hate me for creating 360 images in the directory where you ran the code. But worry not, we’ll clean that up soon.
Next, we want to instruct ffmpeg
to take all our images and combine them into a video.
ffmpeg -start_number 0 -i i-%d.png -pix_fmt yuv420p -movflags +faststart -vcodec libx264 video.mp4
Note that if you don’t include the -pix_fmt
and -movflags
options above, Firefox (and maybe other browsers) might complain with a message saying something like “Video can’t be played because the file is corrupt”. But the above has been tested and works well in Firefox and other browsers.
With -start_number
we’re saying our images start with the number 0, and with i-%d.png
, we instruct ffmpeg
to look for files named in that pattern.
You should now have a slick video of your animation. Once you’ve opened it up and verified it works, you can clean up your directory with:
rm *.png
but be careful to run it in your project directory.
If you followed along this far, you must be interested in some generative art. Have you checked out my Instagram account yet? :)
Thanks for reading and feel free to reach out if you want to talk programming, Go, generative art or anything else I might cover on this blog.