diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..31580cc
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+users.html
diff --git a/genusers.go b/genusers.go
new file mode 100644
index 0000000..8154734
--- /dev/null
+++ b/genusers.go
@@ -0,0 +1,155 @@
+package main
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "html/template"
+ "math/rand"
+ "os"
+ "os/exec"
+ "time"
+)
+
+const statsPath = "/home/vilmibm/bin/townstats"
+
+// TODO const statsPath = "/town/bin/stats"
+
+type user struct {
+ Username string `json:"username"` // Username of user
+ PageTitle string `json:"title"` // Title of user's HTML page, if they have one
+ DefaultPage bool `json:"default"` // Whether or not user has updated their default index.html
+}
+
+type tildeData struct {
+ Users []*user `json:"users"`
+}
+
+func _main() error {
+ data, err := stats()
+ if err != nil {
+ return err
+ }
+
+ users := []*user{}
+
+ for _, u := range data.Users {
+ if !u.DefaultPage {
+ users = append(users, u)
+ }
+ }
+
+ hypertextDocument := getHeader()
+
+ rand.Seed(time.Now().UTC().UnixNano())
+
+ for i := range rand.Perm(len(users)) {
+ link, err := renderUserLink(users[i])
+ if err != nil {
+ return err
+ }
+
+ hypertextDocument += link
+ }
+
+ hypertextDocument += getFooter()
+
+ fmt.Println(hypertextDocument)
+
+ return nil
+}
+
+func renderUserLink(u *user) (string, error) {
+ title := u.PageTitle
+ if title == "" {
+ title = "~" + u.Username
+ }
+ pt := rand.Intn(20)
+ pb := rand.Intn(30)
+ pl := rand.Intn(25)
+ pr := rand.Intn(20)
+ opacity := rand.Intn(50) + 50
+ h := rand.Intn(5)
+ t, err := template.New("userlink").Parse(`