Last updated: February 5, 2025

<span aria-hidden="true" id="generating-audio-waveforms-with-go-a-step-by-step-guide"></span>

# Generating audio waveforms with Go: a step-by-step guide

![Tim Koschützki](/assets/images/teammates/avatar-tim-kos-1.jpg?dpl=dpl_7kibEao2BYN7CAhZRsbwkyFkShjF)

**Tim Koschützki**

Co-founder · Berlin, Germany · Show bio

[](https://x.com/tim%5Fkos)[](https://github.com/tim-kos)

Audio visualization helps developers create better user experiences. This tutorial shows you how to generate waveform images from audio files using Go and modern open-source tools.

<span aria-hidden="true" id="prerequisites"></span>

## Prerequisites

Before starting, ensure you have:

* Go 1.21 or later installed
* Required system dependencies:
  * Linux: libasound2-dev
  * macOS: No additional requirements
  * Windows: No additional requirements

<span aria-hidden="true" id="setting-up-your-go-environment"></span>

## Setting up your Go environment

Create a new Go module and install the required dependency:

```bash
mkdir waveform-generator
cd waveform-generator
go mod init waveform-generator
go get github.com/mdlayher/waveform

```

<span aria-hidden="true" id="basic-waveform-generation"></span>

## Basic waveform generation

Below is a simple implementation using the mdlayher/waveform library:

```go
package main

import (
	"fmt"
	"image/color"
	"image/png"
	"log"
	"os"

	"github.com/mdlayher/waveform"
)

func generateWaveform(inputPath, outputPath string) error {
	f, err := os.Open(inputPath)
	if err != nil {
		return fmt.Errorf("failed to open audio file: %w", err)
	}
	defer f.Close()

	opts := []waveform.Option{
		waveform.Resolution(1024),
		waveform.Scale(2.0),
		waveform.Color(color.RGBA{255, 0, 0, 255}),
		waveform.BackgroundColor(color.White),
	}

	img, err := waveform.New(f, opts...)
	if err != nil {
		return fmt.Errorf("failed to generate waveform: %w", err)
	}

	out, err := os.Create(outputPath)
	if err != nil {
		return fmt.Errorf("failed to create output file: %w", err)
	}
	defer out.Close()

	if err := png.Encode(out, img); err != nil {
		return fmt.Errorf("failed to encode image: %w", err)
	}

	return nil
}

func main() {
	if err := generateWaveform("input.mp3", "waveform.png"); err != nil {
		log.Fatal(err)
	}
	fmt.Println("Waveform generated successfully")
}

```

<span aria-hidden="true" id="advanced-features"></span>

## Advanced features

<span aria-hidden="true" id="streaming-large-files"></span>

### Streaming large files

This example demonstrates how to handle large audio files by streaming data in chunks:

```go
import "os"

func generateStreamingWaveform(inputPath string, chunkSize int64) error {
	file, err := os.Open(inputPath)
	if err != nil {
		return fmt.Errorf("failed to open file: %w", err)
	}
	defer file.Close()

	stat, err := file.Stat()
	if err != nil {
		return fmt.Errorf("failed to get file stats: %w", err)
	}

	if stat.Size() > 100*1024*1024 { // 100MB
		opts := []waveform.Option{
			waveform.Resolution(1024),
			waveform.Scale(1.0),
			waveform.Streaming(true),
			waveform.ChunkSize(chunkSize),
		}

		img, err := waveform.New(file, opts...)
		if err != nil {
			return fmt.Errorf("failed to generate streaming waveform: %w", err)
		}

		out, err := os.Create("large_waveform.png")
		if err != nil {
			return fmt.Errorf("failed to create output file: %w", err)
		}
		defer out.Close()

		return png.Encode(out, img)
	}

	return generateWaveform(inputPath, "waveform.png")
}

```

<span aria-hidden="true" id="custom-colors-and-styling"></span>

### Custom colors and styling

Customize your waveform with specific colors and dimensions:

```go
package main

import (
	"fmt"
	"image/color"
	"image/png"
	"os"

	"github.com/mdlayher/waveform"
)

type WaveformConfig struct {
	Resolution      int
	Scale           float64
	ForegroundColor color.Color
	BackgroundColor color.Color
	Width           int
	Height          int
}

func generateCustomWaveform(inputPath string, config WaveformConfig) error {
	f, err := os.Open(inputPath)
	if err != nil {
		return fmt.Errorf("failed to open audio file: %w", err)
	}
	defer f.Close()

	opts := []waveform.Option{
		waveform.Resolution(config.Resolution),
		waveform.Scale(config.Scale),
		waveform.Color(config.ForegroundColor),
		waveform.BackgroundColor(config.BackgroundColor),
		waveform.Width(config.Width),
		waveform.Height(config.Height),
	}

	img, err := waveform.New(f, opts...)
	if err != nil {
		return fmt.Errorf("failed to generate waveform: %w", err)
	}

	out, err := os.Create("custom_waveform.png")
	if err != nil {
		return fmt.Errorf("failed to create output file: %w", err)
	}
	defer out.Close()

	return png.Encode(out, img)
}

```

<span aria-hidden="true" id="error-handling-and-validation"></span>

## Error handling and validation

Ensure your audio files meet basic criteria before processing.

**Note**

Import `"strings"` and `"path/filepath"` as needed.

```go
import (
	"fmt"
	"os"
	"strings"
	"path/filepath"
)

func validateAudioFile(path string) error {
	stat, err := os.Stat(path)
	if err != nil {
		return fmt.Errorf("failed to stat file: %w", err)
	}

	if stat.Size() == 0 {
		return fmt.Errorf("file is empty")
	}

	if stat.Size() > 500*1024*1024 { // 500MB
		return fmt.Errorf("file too large: %d bytes (max 500MB)", stat.Size())
	}

	ext := strings.ToLower(filepath.Ext(path))
	supportedFormats := map[string]bool{
		".mp3":  true,
		".wav":  true,
		".ogg":  true,
		".flac": true,
	}

	if !supportedFormats[ext] {
		return fmt.Errorf("unsupported audio format: %s", ext)
	}

	return nil
}

```

<span aria-hidden="true" id="testing"></span>

## Testing

Run tests with Go's testing package to ensure waveform generation works as expected:

```go
import (
	"image/color"
	"testing"
)

func TestWaveformGeneration(t *testing.T) {
	tests := []struct {
		name    string
		input   string
		config  WaveformConfig
		wantErr bool
	}{
		{
			name:  "valid mp3",
			input: "testdata/sample.mp3",
			config: WaveformConfig{
				Resolution:      1024,
				Scale:           2.0,
				ForegroundColor: color.RGBA{255, 0, 0, 255},
				BackgroundColor: color.White,
				Width:           800,
				Height:          200,
			},
			wantErr: false,
		},
		{
			name:  "invalid file",
			input: "nonexistent.mp3",
			config: WaveformConfig{
				Resolution: 1024,
				Scale:      2.0,
			},
			wantErr: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := generateCustomWaveform(tt.input, tt.config)
			if (err != nil) != tt.wantErr {
				t.Errorf("generateCustomWaveform() error = %v, wantErr %v", err, tt.wantErr)
			}
		})
	}
}

```

<span aria-hidden="true" id="performance-optimization"></span>

## Performance optimization

Optimize waveform processing by leveraging parallelism. Ensure you import "runtime" as needed.

```go
import "runtime"

func processInParallel(files []string, config WaveformConfig) error {
	workers := runtime.NumCPU()
	jobs := make(chan string, len(files))
	results := make(chan error, len(files))

	for w := 0; w < workers; w++ {
		go func() {
			for file := range jobs {
				err := generateCustomWaveform(file, config)
				results <- err
			}
		}()
	}

	for _, file := range files {
		jobs <- file
	}
	close(jobs)

	for range files {
		if err := <-results; err != nil {
			return fmt.Errorf("failed to process file: %w", err)
		}
	}

	return nil
}

```

<span aria-hidden="true" id="web-service-integration"></span>

## Web service integration

Use the following example to integrate waveform generation into a web service. The code now includes a helper to generate a waveform from an io.Reader.

```go
import (
	"image"
	"image/color"
	"image/png"
	"io"
	"net/http"

	"github.com/mdlayher/waveform"
)

// generateWaveformFromReader creates a waveform image from an io.Reader using the provided config.
func generateWaveformFromReader(r io.Reader, config WaveformConfig) (image.Image, error) {
	opts := []waveform.Option{
		waveform.Resolution(config.Resolution),
		waveform.Scale(config.Scale),
		waveform.Color(config.ForegroundColor),
		waveform.BackgroundColor(config.BackgroundColor),
		waveform.Width(config.Width),
		waveform.Height(config.Height),
	}
	return waveform.New(r, opts...)
}

func handleWaveform(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	file, header, err := r.FormFile("audio")
	if err != nil {
		http.Error(w, "Failed to read file", http.StatusBadRequest)
		return
	}
	defer file.Close()

	// File validation can be extended by checking header.Size or file content if necessary.

	config := WaveformConfig{
		Resolution:      1024,
		Scale:           2.0,
		ForegroundColor: color.RGBA{255, 0, 0, 255},
		BackgroundColor: color.White,
		Width:           800,
		Height:          200,
	}

	img, err := generateWaveformFromReader(file, config)
	if err != nil {
		http.Error(w, "Failed to generate waveform", http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "image/png")
	if err := png.Encode(w, img); err != nil {
		http.Error(w, "Failed to encode image", http.StatusInternalServerError)
	}
}

```

<span aria-hidden="true" id="enhance-your-audio-applications"></span>

## Enhance your audio applications

Generating waveform images from audio files is straightforward with Go and modern open-source tools. These visuals improve user experience and add interactivity to your audio applications.

Looking for a robust solution to handle audio processing and waveform generation at scale?[Transloadit](/index.md) offers powerful APIs and services that streamline your workflow.

\#go#audio-waveform#audio-visualization#open-source#go-library#audio-encoding-service

### 👩‍💻 Join 20k+ developers

Sign up for our [monthly newsletter](/newsletters.md) to receive direct links to 3 exclusive tech — and 2 product updates. No less, no more.

Your email:

Get access

## File uploading and encoding. Made simple.

Transloadit streamlines file handling for developers, trusted by brands like Coursera and The New York Times. We’re known for a reliable API, top-notch support, and a strong commitment to open source, with projects like [Uppy⁠](https://uppy.io) and [Tus⁠](https://tus.io) setting standards in file processing.

[Sign up](/c/)[Book a Demo](https://survey.typeform.com/to/kRg47Xi5)

No credit card needed

Cancel anytime
