Columnist logo: four columns in the colours of the default osu!mania skin

Columnist

A foundation model for osu!mania 4K charts.
It reads a chart as a sequence of note rows, learns from tens of thousands of unlabelled charts, and can then tell you what is in a chart, how hard it is, and which other charts play like it.

Try it in the browser · Dataset · Code

What it does

osu!mania charts are long streams of notes, and the things players care about (jacks, streams, long-note coordination, scroll-speed tricks, how hard it feels) are hard to read off with fixed rules. Columnist first learns what charts look like in general, with no human labels, and is then adapted to specific jobs:

Job What you get Try it
Find similar charts A 256-number fingerprint per chart. Close fingerprints mean charts that play alike. Similar charts
Pattern labels A probability for each of 31 labels: 11 pattern categories (Chordjack, Streams, Jumpstream/Handstream, Technical, Stamina, Release, Coordination, Density/Inverse, Reading, Memo, TB) and 20 osu! user tags. Difficulty & labels
Difficulty A star-like difficulty, a hardest-section indicator and a per-row difficulty curve. Difficulty & labels
Pattern readout while playing Per-note pattern scores shown live as a chart plays. Player

The model only sees note times in milliseconds, long-note ends and visible scroll-speed changes. It uses no snap or BPM-grid information, so live-timed maps work as well as grid-timed ones.

Files

File Purpose
columnist-embed.pt Pretrained encoder (256 dimensions, 6 layers, 6.3M parameters), trained only to model charts themselves. Best choice for similarity and retrieval.
columnist-base.pt Pretrained encoder with the same architecture that also learns to predict whole-chart summary statistics. Best starting point for fine-tuning on your own labels.
joint/ The fine-tuned model for labels and difficulty (PyTorch weights, calibration to stars, inference code and an example script). See joint/README.md.
numpy/joint.npz, numpy/joint.json The same fine-tuned model as plain NumPy weights, with no PyTorch needed. This is what the website runs.
columnist.py, embed_infer.py Model code and chart embedding helper.
chart_tokens.py, prep_dataset.py Turn a 4K .osu file into the model's input arrays.
*.config.json Architecture and pretraining settings.

Quick start

Chart similarity (needs torch, numpy, pandas):

from pathlib import Path
from embed_infer import load_encoder, embed_chart
import chart_tokens, prep_dataset

mode, keys, notes, sv = chart_tokens.parse(Path("chart.osu"))
tok, gap, sv_row, _ = prep_dataset.chart_rows(notes, sv)   # 4K mania charts with at least 64 rows

enc = load_encoder("columnist-embed.pt")
vec = embed_chart(enc, tok, gap, sv_row)                    # 256-number fingerprint
# cosine similarity between two fingerprints = how alike the charts play

Labels and difficulty:

import json, joint_infer
from osu_to_arrays import osu_to_arrays

model = joint_infer.load("joint_plain.final.pt")
out = model.predict(*osu_to_arrays("chart.osu"))
cal = json.load(open("calibration_joint_plain.json"))["linear"]
stars = cal["intercept"] + cal["slope"] * out["difficulty"]  # about 3.886 + 0.140 x score

(Run these from inside joint/, or see joint/example.py for a complete script.)

How a chart is represented

Each chart becomes a list of rows, one per moment where a note starts or a long note ends. A row stores which of the four columns have a tap, a long-note head or a long-note tail, the milliseconds since the previous row, and the scroll-speed multiplier in force. Charts are processed in windows of 512 rows with local attention, so the model never looks more than about 64 rows away from the note it is reading.

Training data

  • Pretraining: 62,161 unlabelled 4K charts (59,361 unique) from mappers with a ranked or tournament map. Only chart ids are shared; the charts remain the property of their mappers.
  • Fine-tuning labels: 8,453 labelled charts, with labels from osu! user tags, tournament mappool slots and tournament staff sheets. See the dataset.

Results

Evaluated with 5-fold cross-validation where all charts of a song stay in the same fold. AP is average precision, averaged over the 11 categories and over the 20 user tags.

Model Categories AP Tags AP
Columnist fine-tuned for labels 0.703 0.581
Columnist joint model (labels and difficulty in one network) 0.698 0.578

Difficulty: the score correlates with official star rating at Spearman 0.94. After a linear calibration the mean absolute error is 0.33 stars. On held-out tournament mappools, it puts a pair of charts from different rounds in the right order 95% of the time.

Similarity: with columnist-embed.pt, a chart's other difficulties from the same song appear among its 5 nearest neighbours 74% of the time (recall@5).

Hand-built statistics fed to gradient-boosted trees score higher on the categories (about 0.75 / 0.60). Columnist trades some of that accuracy for a single network that also serves similarity, difficulty and per-note readout.

Limitations

  • 4K only, and at least 64 rows per chart.
  • Long-note patterns are the weakest area; rare labels such as Memo have few examples.
  • Labels come from user tags and mappool slots, so the outputs say "would players tag this chart this way", not "this pattern is definitely present". Probabilities are good for ranking charts, not calibrated.
  • The difficulty score is learned from chart ordering: use it as a star-like estimate (best between roughly 1 and 11 stars), not as an official rating.
  • Trained on charts up to October 2026; very new styles may be scored unreliably.
  • This is a research tool, not an official osu! tool, and it should not be used on its own for Beatmap Nominator decisions.

Licence and credit

Weights, labels and this card are released under CC BY 4.0, the code under Apache-2.0. Charts are not redistributed. Use of osu! data complies with fair research use.

Komi. Columnist: a foundation model for osu!mania charts. 2026.
Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support