diff --git a/examples/community/procedural-terrain-1d/README.md b/examples/community/procedural-terrain-1d/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..d803a092b3529658eb22a85c9ac433f01f50ca4f
--- /dev/null
+++ b/examples/community/procedural-terrain-1d/README.md
@@ -0,0 +1,8 @@
+# Procedura 1D terrain generator
+
+This is a demo of a 1D terrain generator using [Perlin noise](https://en.wikipedia.org/wiki/Perlin_noise) algorithm.
+Press *space* to generate a random terrain.
+
+Uses [Go-Perlin](https://github.com/aquilax/go-perlin).
+
+Texture by [hh316](https://hhh316.deviantart.com/art/Seamless-stone-cliff-face-mountain-texture-377076626).
diff --git a/examples/community/procedural-terrain-1d/main.go b/examples/community/procedural-terrain-1d/main.go
new file mode 100644
index 0000000000000000000000000000000000000000..242ee430699b7bd0841360d0929da9df522eb1c2
--- /dev/null
+++ b/examples/community/procedural-terrain-1d/main.go
@@ -0,0 +1,106 @@
+package main
+
+import (
+	"image"
+	"log"
+	"math/rand"
+	"os"
+	"time"
+
+	_ "image/jpeg"
+
+	perlin "github.com/aquilax/go-perlin"
+	"github.com/faiface/pixel"
+	"github.com/faiface/pixel/pixelgl"
+	"github.com/svera/pixel/imdraw"
+	"golang.org/x/image/colornames"
+)
+
+const (
+	width  = 800
+	height = 600
+	// Top of the mountain must be around the half of the screen height
+	verticalOffset = height / 2
+	// Perlin noise provides variations in values between -1 and 1,
+	// we multiply those so they're visible on screen
+	scale            = 100
+	waveLength       = 100
+	alpha            = 2.
+	beta             = 2.
+	n                = 3
+	maximumSeedValue = 100
+)
+
+func init() {
+	rand.Seed(time.Now().UTC().UnixNano())
+}
+
+func main() {
+	pixelgl.Run(run)
+}
+
+func run() {
+	cfg := pixelgl.WindowConfig{
+		Title:  "Procedural terrain 1D",
+		Bounds: pixel.R(0, 0, width, height),
+		VSync:  true,
+	}
+	win, err := pixelgl.NewWindow(cfg)
+	if err != nil {
+		panic(err)
+	}
+	pic, err := loadPicture("stone.jpg")
+	if err != nil {
+		panic(err)
+	}
+	imd := imdraw.New(pic)
+
+	drawTerrain(win, imd)
+
+	for !win.Closed() {
+		if win.JustPressed(pixelgl.KeySpace) {
+			drawTerrain(win, imd)
+		}
+		win.Update()
+	}
+}
+
+func loadPicture(path string) (pixel.Picture, error) {
+	file, err := os.Open(path)
+	if err != nil {
+		return nil, err
+	}
+	defer file.Close()
+	img, _, err := image.Decode(file)
+	if err != nil {
+		return nil, err
+	}
+	return pixel.PictureDataFromImage(img), nil
+}
+
+func drawTerrain(win *pixelgl.Window, imd *imdraw.IMDraw) {
+	var seed = rand.Int63n(maximumSeedValue)
+	p := perlin.NewPerlin(alpha, beta, n, seed)
+
+	imd.Clear()
+	win.Clear(colornames.Skyblue)
+	for x := 0.; x < width; x++ {
+		y := p.Noise1D(x/waveLength)*scale + verticalOffset
+		log.Printf("%f\n", y)
+		renderTexturedLine(x, y, imd)
+	}
+	imd.Draw(win)
+}
+
+// Render a textured line in position x with a height y.
+// Note that the textured line is just a 1 px width rectangle.
+// We push the opposite vertices of that rectangle and specify the points of the
+// texture we want to apply to them. Pixel will fill the rest of the rectangle interpolating the texture.
+func renderTexturedLine(x, y float64, imd *imdraw.IMDraw) {
+	imd.Intensity = 1.
+	imd.Picture = pixel.V(x, 0)
+	imd.Push(pixel.V(x, 0))
+	imd.Picture = pixel.V(x+1, y)
+	imd.Push(pixel.V(x+1, y))
+	imd.Rectangle(0)
+}
diff --git a/examples/community/procedural-terrain-1d/stone.jpg b/examples/community/procedural-terrain-1d/stone.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..9899939c6243ed5a0faced1650c16203f839d5ca
Binary files /dev/null and b/examples/community/procedural-terrain-1d/stone.jpg differ