← Back to SmarterPlaylists

Multi-Objective Sequencer

Build perfectly balanced playlists by combining multiple goals

What is the MOS?

The Multi-Objective Sequencer (MOS) is an ordering component that builds a playlist track by track, choosing the best next track at each step based on multiple goals you define.

Most ordering components optimize for a single thing — sort by popularity, sort by tempo, shuffle randomly. The MOS lets you combine many goals at once. For example: "I want high-energy tracks, spread out the artists, keep the tempo between 110–140 BPM, and gradually increase valence." No single sorter can do all of that. The MOS can.

You feed it a pool of candidate tracks (from any source components), configure one or more objectives, and set a limit for how many tracks to output. The MOS then assembles the best playlist it can from the available pool.

About objectives

Objectives are the building blocks of the MOS. Each objective defines a single goal — like "prefer high-energy tracks" or "space out artists." You add one or more objectives to the MOS and assign each a weight that controls how important it is. The MOS then balances all your objectives together when choosing each track.

Every objective operates on a track attribute — a property of a track like popularity, tempo, artist, or energy. You pick which attribute the objective should care about, and the objective uses it to score candidates.

There are 10 objectives, and they fall into a few natural categories:

Value objectives

These score tracks based on a single attribute value, without considering what's already in the playlist:

Sequencing objectives

These are context-aware — they look at what's already been placed in the playlist and score candidates based on how well they fit next. This is what makes the MOS fundamentally different from a simple sort:

A typical MOS configuration combines objectives from both categories. For example, a party playlist might use In Range to keep energy high, Maximize to prefer danceable tracks, and Minimum Separation to prevent the same artist from appearing back-to-back. See the full objectives reference below for details on each one.

Weights and scoring

Every objective has a weight that controls how much influence it has on track selection. Weights range from "off" (ignored) to "unbreakable" (hard constraint).

WeightValueBehavior
off0Objective is completely ignored
lowest0.01Barely considered — only breaks ties
low0.25Minor preference
medium0.5Default — balanced importance
high0.75Strong preference
highest1.0Dominant — will usually win over lower-weighted objectives
unbreakableHard constraint — tracks that violate this are excluded entirely

How scores combine

For each candidate track, the MOS calculates:

total score = (score1 × weight1) + (score2 × weight2) + …

The track with the lowest total score wins. Since each objective returns a score between 0 (best) and 1 (worst), a perfectly fitting track scores near 0.

Unbreakable vs. weighted

Unbreakable objectives work differently from weighted ones. Instead of contributing to the score, they act as hard filters: any track with a non-zero score on an unbreakable objective is disqualified from that position entirely. If no candidates pass all unbreakable constraints, the MOS stops building the playlist — it won't compromise on unbreakable rules.

Note: Some objectives (maximize, minimize, target, order-by) cannot be set to unbreakable because they're ranking objectives — they order tracks from best to worst, so every track except the single best one would be disqualified. Use highest instead for a very strong preference.

The mo_score attribute

Every track output by the MOS gets an mo_score attribute stamped on it (0 = perfect fit, higher = more compromise). You can use this in downstream components — for example, to filter out tracks above a score threshold, or to inspect how well the sequencer achieved its goals.

Slot restrictions

By default, every objective applies to every position in the output playlist. The slots parameter lets you restrict an objective to specific positions. This opens up powerful sequencing patterns — like shaping the energy arc of a playlist, anchoring the opening track, or applying different rules to the beginning and end.

Slot syntax

The slots field accepts a flexible format. Positions are 1-indexed (position 1 is the first track). You can combine any of these in a single field:

FormatExampleMeaning
Single number1Only position 1
Range1-5Positions 1 through 5
Comma-separated1,3,5Positions 1, 3, and 5
Space-separated1 3 5Same as above
Mixed1-3, 7, 10-12Positions 1, 2, 3, 7, 10, 11, 12

Leave the slots field empty (the default) to apply the objective to all positions.

How slots interact with context

Context-aware objectives (order-by, variety, min-separation, max-match) track what's already been placed in the playlist. When an objective has a slot restriction, it only sees tracks from positions within its own slot range as context. This means two objectives of the same type with different slot ranges operate independently.

For example, if you have "order-by energy ascending" for slots 1–25 and "order-by energy descending" for slots 26–50, the descending objective starts fresh at position 26 — it doesn't see the ascending run as context. This is what allows you to create energy arcs, V-shapes, and other non-monotonic patterns.

Tip: Objectives without slot restrictions always see the full playlist context. If you combine a slotted objective with an unslotted one (like a global "variety artist" objective), the unslotted objective still considers every track placed so far.
Note: Slots restrict where an objective has influence, not which tracks it can choose from. The entire remaining pool is always available as candidates. Slots only control which positions the objective's scoring applies to.

How it works

The MOS uses a greedy selection algorithm. It builds the playlist one track at a time:

  1. Start with the full pool of input tracks and an empty result list.
  2. For each position in the playlist (up to your limit):
    1. Determine active objectives — only objectives whose slots include the current position (or that have no slot restriction) participate in this step.
    2. Check unbreakable constraints — any track that violates an active unbreakable objective is removed from consideration for this position.
    3. Score every remaining candidate — each active weighted objective scores every track from 0 (perfect) to 1 (worst). Context-aware objectives only see previous tracks from positions within their slot range. Scores are multiplied by each objective's weight and summed.
    4. Pick the lowest-scoring track — this is the best overall fit across all active objectives.
    5. Remove it from the pool and add it to the result.
  3. Stop when the limit is reached, the pool is empty, or no candidates satisfy all unbreakable constraints.

Because scoring happens at each step, objectives that depend on what's already in the playlist (like variety, min-separation, and order-by) can make context-aware decisions. The MOS doesn't just sort — it sequences.

Objectives reference

Detailed parameters and scoring behavior for each of the 10 objectives. The colored tags indicate what attribute types the objective works with.

Maximize numeric

Prefer tracks with higher values for the chosen attribute.

ParameterDefaultDescription
fieldpopularityThe numeric attribute to maximize
weightmediumImportance (cannot be unbreakable)

Scoring is relative to the actual pool: the highest-value track in the pool scores 0, the lowest scores 1. This means it adapts to whatever tracks you feed in.

Use when: you want the most popular, most energetic, or loudest tracks to be preferred.

Minimize numeric

Prefer tracks with lower values for the chosen attribute.

ParameterDefaultDescription
fieldpopularityThe numeric attribute to minimize
weightmediumImportance (cannot be unbreakable)

The inverse of maximize: the lowest-value track scores 0, the highest scores 1.

Use when: you want deep cuts (low popularity), quiet tracks (low energy), or short songs (low duration).

In Range numeric

Prefer tracks with attribute values inside a given range.

ParameterDefaultDescription
fieldpopularityThe numeric attribute to check
min_val0Minimum value (inclusive)
max_val100Maximum value (inclusive)
weightmediumImportance (can be unbreakable)

Tracks inside the range score 0 (perfect). Tracks outside score proportionally to how far away they are. This makes it a "soft" preference when weighted and a "hard" filter when unbreakable.

Use when: you need tracks within a BPM window (tempo 120–140), a popularity bracket, or a release year decade. Set to unbreakable to enforce a hard cutoff.

Target numeric

Prefer tracks closest to a specific target value.

ParameterDefaultDescription
fieldpopularityThe numeric attribute to target
target50The desired value
weightmediumImportance (cannot be unbreakable)

Scores based on distance from the target: a track at exactly the target value scores 0, and the track furthest from the target scores 1.

Use when: you want tracks near a specific tempo (target 128 BPM), a sweet spot of energy (target 0.7), or medium popularity (target 50).

Match text / numeric

Prefer tracks that exactly match a given value.

ParameterDefaultDescription
fieldartistThe attribute to match on
value(empty)The exact value to match
weightmediumImportance (can be unbreakable)

Binary scoring: tracks that match score 0, everything else scores 1. Comparison is exact string equality.

Use when: you want to prefer (or require) tracks from a specific artist, on a specific album, from a specific country, or in a specific key.

Contains text list

Prefer tracks where the attribute contains a given value (substring or list membership).

ParameterDefaultDescription
fieldartistThe attribute to check
value(empty)The value to look for
weightmediumImportance (can be unbreakable)

For text attributes, checks if the value appears as a substring. For list attributes (like mb_genres), checks if the value is a member of the list. Matching tracks score 0, non-matching score 1.

Use when: you want to favor tracks that belong to a genre ("rock" in mb_genres), or whose artist name contains a keyword.

Max Match text / numeric

Limit how many tracks can share the same value for an attribute.

ParameterDefaultDescription
fieldartistThe attribute to limit
max_count1Maximum tracks allowed with the same value
weightmediumImportance (can be unbreakable)

Tracks score 0 if fewer than max_count tracks with the same value are already in the playlist; otherwise they score 1. This is context-aware — it checks what's already been selected.

Use when: you want at most 2 tracks per artist, no more than 3 tracks from the same album, or exactly 1 track per label. Set to unbreakable to enforce a hard cap.

Order By text / numeric

Prefer tracks that continue in ascending (or descending) order.

ParameterDefaultDescription
fieldpopularityThe attribute to order by
inversefalseIf true, prefer descending instead of ascending
spreadtrueIf true, select tracks spanning the full range of values
weightmediumImportance (cannot be unbreakable)

This is context-aware: it looks at the last track placed and prefers tracks that continue in the same direction. Tracks that continue the order score low; tracks that reverse it score high.

When spread is enabled, the objective uses rank-based selection to pick tracks that represent the full distribution of values. Instead of clustering at one end of the range (which happens when the pool is much larger than the playlist), it picks tracks at evenly-spaced rank positions through the sorted pool. This works well with any distribution shape — uniform, bimodal, or skewed.

Use when: you want energy to build over the playlist, tempo to gradually increase, or popularity to wind down from hits to deep cuts. Enable spread when feeding in a large pool and you want the output to represent the full range, not just a narrow slice.

Minimum Separation text / numeric

Ensure tracks sharing the same value are spaced apart in the playlist.

ParameterDefaultDescription
fieldartistThe attribute to enforce separation on
separation5Minimum number of tracks between same values
weightmediumImportance (can be unbreakable)

Checks the last N positions in the playlist (where N = separation). If the same value appeared recently, the score increases as the match gets closer. A match right next to the current position scores 1 (worst); a match exactly at the separation distance scores near 0.

Use when: you don't want the same artist back-to-back, or you want to space out tracks from the same genre, album, or era.

Variety text numeric list

Maximize diversity of an attribute across the playlist.

ParameterDefaultDescription
fieldartistThe attribute to diversify
inversefalseIf true, prefer similarity instead of variety
weightmediumImportance (can be unbreakable)

Scoring adapts based on the attribute type:

Set inverse to true to flip the behavior — prefer similarity and clustering instead of variety.

Use when: you want maximum artist diversity, a wide spread of tempos, or genre variety across the playlist. Use inverse when you want genre clustering or to keep similar tempos together.

Usage examples

Here are some real-world scenarios showing how to combine objectives. Each example starts with a goal and lists the objectives you'd configure.

Party playlist: high energy, good variety

Goal: upbeat, danceable tracks with no artist repeats back-to-back.

DJ set: smooth tempo transitions

Goal: tracks that flow together with gradually increasing tempo and compatible keys.

The inverse variety on camelot_num clusters tracks with similar keys together, making transitions smoother.

Discovery mix: deep cuts, spread across genres

Goal: lesser-known tracks from many different genres and eras.

Chill focus playlist: narrow energy and tempo window

Goal: calm, instrumental tracks at a steady tempo for concentration.

Balanced sampler: fair representation

Goal: no more than 2 tracks per artist, balanced across different labels.

Popularity at lowest acts as a tiebreaker — when all else is equal, prefer the more popular track.

Examples using slot restrictions

Energy arc: build up, then wind down

Goal: a 50-track playlist that rises in energy for the first half and fades out in the second half.

The two order-by objectives operate independently — the descending half starts fresh from the highest available energy, not from where the ascending half left off. The min-separation objective has no slots, so it applies globally across the entire playlist.

Anchor the opener and closer

Goal: start with a specific artist, end with a mellow track, fill the middle with variety.

The match and range objectives only fire at positions 1 and 50 respectively, so they don't constrain the middle of the playlist at all.

Genre blocks: rock first, then electronic

Goal: a playlist that starts with rock tracks and transitions to electronic in the second half.

The contains objectives steer genre selection within their slot ranges. The global variety and order-by objectives apply throughout, keeping artists spread out and energy building across both halves.

Tempo ramp with a plateau

Goal: gradually increase tempo for the first 20 tracks, hold steady in the middle, then slow back down.

Three distinct phases: a ramp up, a plateau held within a 10 BPM window, and a ramp down. Each phase's context is independent, so the descending ramp starts from the highest available tempo — not from where the plateau left off.

Best practices

Start simple, then add objectives

Begin with one or two objectives and preview the result. Add more objectives only when you see specific problems to fix. Too many competing objectives can dilute each other, leaving you with a mediocre compromise.

Use unbreakable sparingly

Unbreakable constraints are powerful but rigid. Each unbreakable objective shrinks the candidate pool. Stack too many and the MOS will run out of eligible tracks and stop early, giving you a shorter playlist than your limit. Reserve unbreakable for things you truly won't compromise on (like "no duplicate artists").

Use weight differences to express priority

If two objectives matter but one matters more, don't set both to high. Set the primary one to high and the secondary to medium or low. The weight ratio is what determines influence. Two objectives at the same weight will fight each other equally.

Use lowest for tiebreakers

An objective at lowest weight (0.01) barely affects scoring — but when multiple tracks tie on your main objectives, it decides the winner. Great for a default like "prefer popular tracks when all else is equal."

Feed it a big pool

The MOS can only choose from what you give it. A pool of 500 tracks with a limit of 50 gives the algorithm room to be selective. A pool of 60 tracks with a limit of 50 leaves almost no room to optimize. For best results, give the MOS at least 3–5x more input tracks than your target limit.

Context-aware objectives are your superpower

Objectives like variety, min-separation, order-by, and max-match look at what's already in the playlist. These are what make the MOS fundamentally different from a filter+sort pipeline. Use them for sequencing goals that depend on track order and relationships.

Check mo_score to debug

After running, preview the output and look at the mo_score attribute. Scores near 0 mean tracks fit your objectives well. If scores start climbing toward the end, it means the pool is running low on good candidates — consider loosening your constraints or providing a larger input pool.

Combine MOS with other components

The MOS works well as part of a larger program. Use filters upstream to narrow the pool (remove explicit tracks, limit to a genre), then let the MOS handle the sequencing.