← 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.

Reading the MOS node on the canvas

On the graph canvas, the MOS node displays a human-readable summary of all active objectives directly in the node body, with attribute names in bold. Non-default weights and slot restrictions are shown alongside each objective. This lets you see at a glance what the MOS is doing without opening the editor. If more than six objectives are configured, the display is truncated — double-click the node to see and edit the full list.

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 14 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 Match Max Run to allow short runs of the same artist but force a cooldown after. 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
Section1/4First quarter of the playlist
Section (mixed)1/2, 45-50First half plus positions 45–50

Section syntax

The section format n/N divides the playlist into N equal sections and selects section n. Sections are resolved against the playlist limit parameter. For a 32-track playlist:

Sections distribute positions as evenly as possible. When the limit doesn't divide evenly, the last section absorbs the remainder. For example, with a limit of 10 and 3/3, section 3 gets positions 7–10 (four tracks instead of three).

You can freely mix section syntax with ranges and single positions: 1/4, 30, 40-45.

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, match-max-run, range-max-run) 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 14 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
spreadfulloff = sequential, full = span the full range, segment = each slot segment spans the full range
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.

When spread is set to segment, each contiguous slot segment independently spans the full value range. With full spread, a single order-by objective across multiple segments distributes its picks sequentially — the first segment gets low-ranked values and later segments get higher ones. With segment, every segment covers the full range on its own. This is useful when you want the same energy arc repeated in multiple sections of the playlist.

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.

Match Max Run text / numeric

Limit consecutive runs of tracks that share a value, with a mandatory separation gap before that value can appear again.

ParameterDefaultDescription
fieldartistThe attribute to check
value(empty)Specific value to match. Leave empty for "general match" — the rule applies to any repeated value.
max_run3Maximum consecutive matching tracks allowed
separation5Required non-matching tracks after a maxed-out run before matching tracks are allowed again
weightmediumImportance (can be unbreakable)

This objective combines run-length limiting with a cooldown period. Once a run of matching tracks hits max_run, the objective penalizes further matches until separation non-matching tracks have been placed. During the cooldown, scoring is gradient — tracks closer to completing the separation period get a lower (better) penalty.

General vs. specific match

Use when: you want to prevent artist fatigue (general match on artist), limit consecutive tracks from the same decade, or control how often a specific genre or attribute value appears in runs. Compared to Min Separation, this objective allows short runs before enforcing the gap — Min Separation prevents any repeat within the window.

Range Max Run numeric

Limit consecutive runs of tracks whose attribute falls within (or outside) a numeric range, with a mandatory separation gap.

ParameterDefaultDescription
fieldpopularityThe numeric attribute to check
low0Lower bound of range (inclusive)
high100Upper bound of range (inclusive)
invertfalseIf true, match values outside the range instead
max_run3Maximum consecutive matching tracks allowed
separation5Required non-matching tracks after a maxed-out run
weightmediumImportance (can be unbreakable)

Works like Match Max Run but matches based on a numeric range instead of exact value. A track "matches" if its attribute value falls within [low, high] (or outside if invert is true). Once max_run consecutive matches occur, further matches are penalized until separation non-matching tracks have been placed.

Use when: you want to prevent too many high-energy tracks in a row ("max 3 tracks with energy > 0.8, then 2 calmer ones"), limit runs of obscure tracks, or alternate between tempo zones. Use invert to limit runs of tracks outside a range.

Continuity text numeric list

Score candidates by distance from the last selected track's attribute value.

ParameterDefaultDescription
fieldenergyThe attribute to measure continuity on
invertfalseIf true, prefer tracks furthest from the last track
weightmediumImportance (can be unbreakable)

Scoring adapts based on the attribute type:

Set invert to true to prefer tracks that are distant from the last track instead.

Use when: you want smooth transitions in energy, tempo, or mood between consecutive tracks. Use invert when you want maximum contrast between adjacent tracks.

Distribution text numeric list

Target a desired percentage distribution of attribute values in the output playlist.

ParameterDefaultDescription
fieldartist_genderThe attribute to control distribution for
values(empty)Space-separated value(N%) pairs (see below)
weightmediumImportance (cannot be unbreakable)

Values syntax

The values field uses a compact format: each entry is a value followed by a target percentage in parentheses, separated by spaces. Three match modes are auto-detected from the token shape:

Percentages don't need to sum to 100%. Unspecified values act as filler, giving other objectives more room. Percentages can exceed 100% total when buckets overlap or when using list fields where a single track can match multiple buckets.

How scoring works

At each step, the objective simulates adding each candidate to the playlist and measures how close the resulting percentages would be to the targets. The candidate that brings the actual distribution closest to the desired distribution scores 0 (best); the one that moves it furthest away scores 1 (worst).

Examples

Use when: you want to control the demographic or categorical makeup of a playlist — gender balance, genre proportions, era distribution, or country representation.

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.

Gender-balanced mix with genre targets

Goal: a playlist that's roughly 40% female artists, 40% male, with genre diversity and decent popularity.

The 20% gender slack and 25% genre slack give the algorithm room to satisfy both distribution objectives. Popularity at lowest breaks ties.

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.

Using section syntax (1/2 and 2/2) instead of hard-coded ranges means this works regardless of the playlist limit — change the limit from 50 to 100 and the split point adjusts automatically. The two order-by objectives operate independently — the descending half starts fresh from the highest available energy. The min-separation objective has no slots, so it applies globally.

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. Using 1/2 and 2/2 keeps the split proportional to the playlist size. The global variety and order-by objectives apply throughout.

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. You could also use 1/3, 2/3, 3/3 for equal thirds that adapt to any limit. 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, max-match, match-max-run, range-max-run, continuity, and distribution 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.