Skip to content
Tutorials6 min read

Music Genre Classification Annotation

Build a music tagging task in Potato with audio playback, track metadata on screen, multi-label genre tagging, and Likert scales for energy and danceability.

Potato Team

Genre labels are what recommendation systems, auto-generated playlists, and music discovery features run on. This post builds genre-tagging interfaces in Potato, with audio previews and the track metadata an annotator needs to judge what they are hearing.

Playing the track and asking the question

Potato keeps content and questions apart. instance_display says what the annotator sees and hears, and annotation_schemes says what they answer. For a tagging task the audio is a display field, not an annotation scheme:

yaml
annotation_task_name: "Music Genre Classification"
task_dir: "."
 
data_files:
  - data/tracks.json
 
item_properties:
  id_key: track_id
  text_key: audio_preview
 
instance_display:
  fields:
    - key: artist
      type: text
      label: "Artist"
 
    - key: title
      type: text
      label: "Track"
 
    - key: audio_preview
      type: audio
      label: "Preview"
      display_options:
        controls: true
        show_waveform: true
 
annotation_schemes:
  - annotation_type: radio
    name: primary_genre
    description: "Select the primary genre"
    labels:
      - Rock
      - Pop
      - Hip-Hop
      - Electronic
      - Jazz
      - Classical
      - R&B
      - Country
      - Metal
      - Folk
    sequential_key_binding: true
 
output_annotation_dir: annotation_output/
export_annotation_format: jsonl

item_properties takes id_key and text_key only. There is no audio_path or metadata_fields key, and a config that uses them gets a player that never appears and metadata the annotator never sees. Anything the annotator should read is a field in instance_display.

The audio_annotation type is for marking regions of the waveform. A whole-track judgment is a radio, a multiselect, or a likert.

A two-level genre taxonomy

Potato has no conditional label tree, so a two-level taxonomy is two schemes, and the second needs every subgenre it might have to cover:

yaml
annotation_schemes:
  - annotation_type: radio
    name: genre
    description: "Primary genre"
    labels:
      - Rock
      - Electronic
      - Hip-Hop
      - Pop
      - Jazz
      - Classical
 
  - annotation_type: radio
    name: subgenre
    description: "Subgenre within the primary genre"
    labels:
      - Indie Rock
      - Punk
      - Metal
      - House
      - Techno
      - Drum & Bass
      - Boom Bap
      - Trap
      - Synth-pop
      - Bebop
      - Baroque
      - Romantic
      - Other

Where that list gets unwieldy, hierarchical_multiselect collapses both levels into one widget instead.

Multi-label tagging

Plenty of tracks sit in more than one genre:

yaml
annotation_schemes:
  - annotation_type: multiselect
    name: genres
    description: "Select every genre that applies, up to three"
    labels:
      - Rock
      - Pop
      - Electronic
      - Hip-Hop
      - R&B
      - Jazz
      - Classical
      - Country
      - Folk
      - Metal
      - Punk
      - Indie
      - World
      - Latin
 
  - annotation_type: radio
    name: primary_genre
    description: "Which of the genres you selected is the primary one?"
    labels:
      - Rock
      - Pop
      - Electronic
      - Hip-Hop
      - R&B
      - Jazz
      - Classical
      - Country
      - Folk
      - Metal
      - Punk
      - Indie
      - World
      - Latin

Nothing enforces the "up to three" or checks that the primary genre is one of the selected ones. Both are worth stating in the instructions and checking in the export.

Complete configuration

yaml
annotation_task_name: "Music Tagging and Classification"
task_dir: "."
 
data_files:
  - data/music_library.json
 
item_properties:
  id_key: track_id
  text_key: preview_url
 
instance_display:
  layout:
    direction: vertical
    gap: 12px
 
  fields:
    - key: artist
      type: text
      label: "Artist"
 
    - key: title
      type: text
      label: "Track"
 
    - key: album
      type: text
      label: "Album"
 
    - key: year
      type: text
      label: "Year"
 
    - key: preview_url
      type: audio
      label: "Preview"
      display_options:
        controls: true
        show_waveform: true
 
annotation_schemes:
  - annotation_type: multiselect
    name: genres
    description: "Select all applicable genres"
    labels:
      - Rock
      - Pop
      - Electronic/Dance
      - Hip-Hop/Rap
      - R&B/Soul
      - Jazz
      - Classical
      - Country
      - Folk/Acoustic
      - Metal
      - Punk
      - Indie
      - World/International
      - Latin
      - Blues
      - Reggae
 
  - annotation_type: multiselect
    name: mood
    description: "Select the mood or moods of this track"
    labels:
      - Happy/Uplifting
      - Sad/Melancholic
      - Energetic
      - Calm/Relaxing
      - Aggressive
      - Romantic
      - Dark/Moody
      - Nostalgic
      - Motivational
 
  - annotation_type: likert
    name: energy
    description: "Energy level"
    size: 5
    min_label: "Very low energy"
    max_label: "Very high energy"
 
  - annotation_type: likert
    name: danceability
    description: "How danceable is this track?"
    size: 5
    min_label: "Not danceable"
    max_label: "Very danceable"
 
  - annotation_type: radio
    name: vocals
    description: "Vocal content"
    labels:
      - Instrumental only
      - Mostly instrumental
      - Balanced
      - Mostly vocals
      - Vocals dominant
 
  - annotation_type: radio
    name: era
    description: "Musical era or style"
    labels:
      - Pre-1970s
      - 1970s
      - 1980s
      - 1990s
      - 2000s
      - 2010s
      - 2020s
 
  - annotation_type: multiselect
    name: flags
    description: "Special flags, if any apply"
    labels:
      - Explicit content
      - Live recording
      - Remix or cover
      - Holiday/Seasonal
      - Soundtrack
      - Spoken word sections
    label_requirement:
      required: false
 
  - annotation_type: likert
    name: confidence
    description: "Confidence in your genre classification"
    size: 5
    min_label: "Uncertain"
    max_label: "Very confident"
 
annotation_instructions: |
  ## Music Tagging Guidelines
 
  ### Genre selection
  Choose one to three genres that describe the track. Prefer the most
  specific genre that fits, and fall back to a broader one when unsure.
 
  ### Mood
  Judge the overall emotional tone. A track can carry several moods.
 
  ### Energy and danceability
  Energy is tempo, intensity, and power. Danceability is rhythm, beat
  clarity, and groove. A slow track can be highly danceable.
 
  ### Listening
  Listen to at least 30 seconds, and listen past the intro. Drops and
  transitions are where the genre often becomes clear.
 
output_annotation_dir: annotation_output/
export_annotation_format: jsonl
 
user_config:
  allow_all_users: true

What the output contains

Each scheme stores its answer under its own name:

json
{
  "genres": ["Rock", "Pop"],
  "mood": ["Happy/Uplifting", "Nostalgic"],
  "energy": "3",
  "danceability": "2",
  "vocals": "Balanced",
  "era": "Pre-1970s",
  "flags": [],
  "confidence": "5"
}

Likert answers come back as strings. Convert them before averaging.

Tips for music annotation

Good headphones matter, because laptop speakers hide the low end that separates several of these genres. Keep the volume steady rather than adjusting per track, which tires ears faster than the listening does. Listen far enough into each preview to catch the changes. Brush up on what each genre label means for your project before starting, since genre boundaries are exactly where annotators disagree. And take breaks, because tired ears make worse calls and the drift is gradual enough that nobody notices it happening.

Where to go next

For the full set of audio annotation options, see the source documentation.


Full audio documentation at Audio Annotation.