Clark, Zhang, Ma, Salehi, Tripathi et al. (Allen Institute for AI & UW), 2026

Molmo2: Open Video VLMs
That Point and Track

A fully-open vision-language model that doesn't just describe a video — it tells you when and where things happen, by emitting spatio-temporal points in space and time. No distillation from closed models, all data released.

Prerequisites: Transformers & attention + basic vision-language models (ViT + LLM)
10
Chapters
7
Simulations

Chapter 0: The Problem

Ask a video model: "How many times does the robot grasp the red block?" A good model answers "three." But where? When? A robot acting on that answer needs the exact moment and pixel of each grasp — not a sentence about it.

This is grounding: the model's output must point back into the pixels and the timeline, not just into language. In a single image, grounding is now routine — models draw boxes and click points. In video, almost nothing does it. The strongest video models (GPT-5, Gemini 3 Pro, Claude) are proprietary — closed weights, closed data, closed recipe — and even they ground video weakly. The open community had no foundation to build on.

The core question: Can we build a fully open video-language model — open weights, open data, open code, and crucially trained without distilling from any closed model — that not only understands video but localizes events in space and time by pointing and tracking?

Why "no distillation" is the hard constraint

The easy way to make an open model strong is to ask GPT-5 a million questions about videos and train on its answers. That is distillation — and it is a trap. You inherit the closed model's blind spots, you can't release the data legally, and you can never surpass the teacher. Worse: closed models can't ground video, so they have nothing to teach about pointing.

Molmo2's bet is that the bottleneck was never the architecture — it was the data. So the bulk of the work is nine new human-and-synthetic datasets built from scratch, plus a handful of training tricks (bidirectional vision attention, message-tree packing, token weighting) that let those datasets actually train efficiently.

A worked sense of scale

Why is video so much harder than images for an LLM-based model? It comes down to how many tokens the language model has to chew through.

InputTokens (rough)Why it hurts
One sentence of text~20Trivial
One image (multi-crop)~700A ViT + connector turns pixels into hundreds of tokens
One 60s video @ 2 fps, 128 frames~16,000+Each frame is its own little image; they stack up fast
Long-context video (384 frames)36,000+Now you need context-parallelism across 8 GPUs

That last column is the entire engineering tension. A video is not "an input" — it is a flood of tens of thousands of visual tokens, most of which are nearly redundant. Everything Molmo2 does about packing, frame sampling, and pooling exists to keep that flood trainable.

What capability is Molmo2's central claim, that even proprietary video models barely support?

Chapter 1: The Key Insight

How do you make a language model — a thing that emits text tokens — point at a pixel? The clever move, inherited from the original Molmo, is to make the point itself be text. The model doesn't have a special "pointing head." It just learns to emit a string like x=43.2 y=61.0 and you read the coordinates back out.

Molmo2 extends this 2D image idea into the temporal dimension. A video point gains a timestamp and an object ID. Now a single text string can say: "at t=2.5s, at pixel (43, 61), this is object #2". String together many such points and you have a track — the same object across many frames. Count the distinct IDs and you have counting. One unified output format does pointing, tracking, and counting.

The insight: Grounding doesn't need new architecture — it needs the answer to be structured text the model can generate autoregressively. A point is (x, y, t, id) emitted as plain tokens. Tracking is just "keep emitting the same id across timestamps." This means a standard ViT+LLM, trained on the right data, can ground video.

Three things the model produces

Free-form language
Captions, answers — "the player celebrates by sticking two fingers up toward the seats."
Points (space + time)
"Point to every car that overtakes" → a list of (x, y, t) per event.
Tracks (points + ids)
"Track all dancers moving left to right" → per-frame points sharing an object id.

The whole system in one breath

Molmo2 is: sample frames from the video → a ViT encodes each frame into patch features → a connector pools and projects them into visual tokens → an LLM reads those tokens interleaved with timestamps and the question → it generates either prose or a coordinate string. We'll build each piece, and along the way meet the three training tricks (bidirectional attention, packing, token weighting) that make 16k-token video sequences trainable at all.

How does Molmo2 let a text-generating LLM "point" at a location?

Chapter 2: The Point Format — Coordinates as Tokens

Let's get concrete about what the model actually writes. For the query "Point to the waterfalls," Molmo2 emits something like:

<points coords
  t=0.5 id=1 x=43.2 y=61.0
  t=0.5 id=2 x=78.1 y=55.4
  t=2.5 id=1 x=44.0 y=60.2
>waterfall</points>

Every symbol earns its place. x, y are normalized to [0, 100] — a percentage of width and height, so they don't depend on the resolution of the crop the model happened to see. t is the timestamp in seconds (for an image set it's an image index instead). id is an integer that is unique per distinct object — this is the magic that turns a pile of points into tracks and counts.

Why normalized [0,100], not raw pixels? The model sees crops at different resolutions (8 crops in training, up to 24 at inference). If it emitted raw pixel coordinates, the same object would have different "correct" answers depending on crop size. Normalizing to percent-of-frame makes the target invariant to resolution — the model learns where in the frame, not which pixel of which crop.

How tracking and counting fall out for free

Look at the id column above. Rows with id=1 at t=0.5 and t=2.5 are the same waterfall at two moments — connect them and you have a track. The number of distinct ids (here, 2) is the count. Points are sorted by time, then x, then y, so the output is deterministic and easy to score.

Try it below: place objects across frames and watch the exact text string the model would have to produce. Notice how adding a frame for an existing id extends a track, while a brand-new id bumps the count.

Build a point stringInteractive

Pick a frame (timestamp), pick an object id, then click on the frame to drop a point. The model's exact text output updates live below.

frame: object id:
<points coords ...>
Distinct ids = 0 (count), tracks = 0
In the point format, what single field turns a list of points into both tracks and counts?

Chapter 3: The Architecture — From Frames to Tokens

Molmo2 deliberately uses a boring architecture: a pre-trained ViT + a pre-trained LLM glued by a connector. The novelty is in the data and training, not the blocks. But to understand the training tricks you must see exactly how a video becomes tokens. Let's trace the tensor shapes.

Step 1 — Frame sampling

A 10-minute video has ~18,000 frames. You cannot feed them all. Molmo2 samples at S = 2 fps with a cap of F = 128 frames (or 384 in the long-context stage). If the video is longer than F/S seconds, it uniformly subsamples down to F frames — and always keeps the last frame, because video players freeze on the final frame, so it often matters to the user.

Worked example: A 5-minute (300s) video at 2 fps would be 600 frames — over the 128 cap. So Molmo2 keeps every 600/128 ≈ 5th sampled frame, landing on 128 evenly-spaced frames spanning the full clip, with the true last frame forced in.

Step 2 — ViT encodes each frame

Each sampled frame is treated like a single image crop (downscaled to fit), run through the ViT, producing patch features. For an image, Molmo2 also tiles it into up to K overlapping crops for higher resolution (K=8 train, K=24 inference); for video frames it uses one crop each to save compute.

Step 3 — The connector pools patches into tokens

This is where the token count gets controlled. The connector takes patch features and pools spatial windows into single vectors using a tiny multi-head attention layer (the mean of the patches is the query). The pooling window size is the key dial:

Same connector weights for both — the only difference is the window size. After pooling, a shared MLP projects each pooled vector into the LLM's token space.

Step 4 — Interleave with text and feed the LLM

The visual tokens enter the LLM interleaved with text timestamps (for video) or image indices (for multi-image), plus frame-start tokens and, when available, subtitles placed as text after the frames. Explore the full pipeline below — drag the frame count and pooling window and watch the final LLM token count, the number that decides whether training fits in memory.

Video → tokens pipelineInteractive

Adjust frame count and pooling window. Watch how the LLM's visual-token budget explodes with frames and shrinks with coarser pooling.

Why do video frames use a 3×3 pooling window while images use 2×2?

Chapter 4: Bidirectional Attention on Vision Tokens

Here is a subtle but high-impact choice. An LLM is causal by default: token t may only attend to tokens before it. That makes sense for generated text — you can't peek at words you haven't written. But it makes no sense for the image tokens, which are all given to the model at once, up front.

Think about it: the token for the top-left patch of frame 5 has no reason to be blind to the bottom-right patch of frame 5 — they are the same frame, presented simultaneously. Forcing causal masking on vision tokens cripples the model's spatial understanding for no benefit.

The fix: Let visual tokens forward-attend to one another — even across different frames or images. So all the image/frame tokens form one fully-connected block where everyone sees everyone, while the text tokens stay causal (a generated word still can't see future words). The paper finds this "yields notable gains."

Trace the data flow: what changes in the mask

The attention mask is a N×N matrix of 0/1 (1 = allowed to attend). For a sequence of [visual tokens | text tokens]:

Toggle the mode below and watch the mask change. The newly-lit cells in the upper-left are exactly the "let vision see vision" gains.

Causal vs bidirectional vision attentionInteractive

Pink rows/cols are visual tokens, gray are text. A lit cell (row i, col j) means token i may attend to token j. Flip the toggle to see the bidirectional vision block light up.

Why does Molmo2 use bidirectional attention for vision tokens but keep text causal?

Chapter 5: Packing & Message Trees — the 15× trick

This is the engineering heart of the paper, and the SHOWCASE. The problem: training examples range from a few hundred tokens (a short text question) to 16,000+ (a long video with subtitles). If you batch them naively, you must pad every example up to the longest one — wasting enormous compute on padding tokens that teach the model nothing.

Two ideas fix this, and they compose:

Idea 1 — Sequence packing

Instead of one example per row, concatenate several short examples into one long sequence until you hit the max length (16,384). No padding. The catch: example A's tokens must not attend to example B's tokens — they're unrelated. So you build a block-diagonal attention mask: each example is its own island.

Idea 2 — Message trees

A single video usually has many annotations — a caption, three Q&A pairs, a pointing task. Re-encoding the expensive video tokens once per annotation would be insane. So Molmo2 encodes the video once as the root, and each annotation becomes a branch hanging off it. The branches share the video but are masked so they can't see each other (otherwise answer #2 could cheat off answer #1).

The payoff, in their numbers: Examples average ~4 annotations. With packing + message trees, ~3.8 examples fit into a 16,348-token sequence during SFT, for roughly 15× training efficiency versus the naive one-example-per-sequence approach. The same video tokens are reused across all its branches instead of recomputed.

The combined attention mask — build it yourself

This is Figure 3 of the paper, made interactive. The full mask must enforce three rules at once: (1) different packed examples are isolated; (2) within one example, the shared visual tokens are seen by all branches; (3) different branches (QA pairs) of the same example can't cross-attend. Toggle each rule and watch the mask — and the wasted-padding meter — respond. Then try turning packing off to feel the pain.

Message-tree packed attention mask (Figure 3, live)Showcase

A packed sequence: Example 1 (frame tokens + QA₁ + QA₂) followed by Example 2 (frame tokens + QA₁). Lit cell (i,j) = token i attends to token j. Toggle the rules and watch utilization climb.

Why this is non-trivial for VLMs (not just LLMs): Packing text is easy — concatenate token ids. But here you must pack both the ViT crops (so the vision encoder runs efficiently) and the LLM tokens, while supporting custom masks. The long-context stage even shards the packed sequence across 8 GPUs with Ulysses attention, chosen precisely because it tolerates these arbitrary custom masks where simpler tensor-parallel attention would not.
What is the purpose of the message-tree structure when a video has multiple annotations?

Chapter 6: Token Weighting — balancing the loss

Now a problem that's pure loss-function design. Molmo2 trains on wildly different output lengths: a multiple-choice answer is 1 token ("B"); a dense video caption is 4,000+ tokens. The training loss sums over output tokens. So even if you sample captions rarely, their 4,000 tokens each drown out the single-token MCQ examples — the model gets fantastic at captioning and forgets how to answer short questions.

The failure mode, stated plainly: "Loss tokens" ≠ "examples." A handful of long captions can contribute more loss than thousands of short answers. Optimize that loss and you optimize for the long tail by accident. Short-answer accuracy degrades.

The fix: weight each example's loss

Molmo2 multiplies each example's loss contribution by a weight chosen to balance long vs short. The recipe:

The is the important shape. A linear weight (1/n) would make every example contribute exactly equally regardless of length — but then a 1-token MCQ gets the same total say as a 100-token answer, over-weighting trivia. A square-root sits between "sum all tokens" (length-dominated) and "one vote per example" (length-blind): longer outputs still matter more, but sub-linearly.

Worked example. Compare a 1-token MCQ and a 100-token answer under three schemes, normalizing the MCQ to 1.0:
Sum of tokens (no weighting): MCQ contributes 1, the 100-token answer contributes 100 — a 100× gap.
√(4n) shape: MCQ → √4 = 2, answer → √400 = 20 — a 10× gap. Length still matters, but the long example no longer dominates.
Per-example (1/n): both contribute 1 — a gap, ignoring that the longer answer genuinely has more to learn.
How weighting reshapes total loss contributionInteractive

Drag output length and switch the weighting scheme. The bar shows each example's total contribution to the loss relative to a 1-token answer.

Code — the core weighting in PyTorch:
# per-token cross-entropy, then a per-example weight
def weighted_loss(logits, targets, task, n_answer_tokens):
    # logits: [B, T, V], targets: [B, T], mask: which tokens are "answer"
    tok_loss = F.cross_entropy(
        logits.view(-1, logits.size(-1)),
        targets.view(-1), reduction='none')        # [B*T]
    n = n_answer_tokens                                  # tokens in THIS example's answer
    if   task == 'video_caption': w = 0.1
    elif task == 'pointing':      w = 0.2
    else:                          w = math.sqrt(4 * n) / n  # sub-linear: per-token weight
    return (tok_loss * w).sum()                       # total ∝ sqrt(4n), not n
Why does Molmo2 weight loss by roughly √(4n) rather than 1/n (one vote per example)?

Chapter 7: The Data Engine — no closed teacher

The architecture is intentionally ordinary; the data is the contribution. Molmo2 builds nine new datasets (five human-annotated, four synthetic) plus two curated from academic sources — all without ever querying a proprietary model for answers. Let's see the cleverest pipelines, because each one is a lesson in getting quality data cheaply.

Dense captions: speak, don't type

Humans type lazy captions but speak rich ones. So annotators narrate each clip aloud; Whisper transcribes; an LLM rewrites for coherence; then frame-level details from the original Molmo are merged in so nothing is missed. Result: an average of 924 words per video — versus 547 in LLaVA-Video and 280 in ShareGPT4Video. Density is the moat.

Long QA: human-and-LLM loop, not distillation

To avoid distilling, the answer isn't taken from a closed model. An LLM proposes an initial answer conditioned on a Molmo2 caption; the human annotator then accepts or iteratively refines it via dialogue. The human is the authority; the LLM is a drafting assistant. Counting questions are deliberately removed from QA — because counting should be answered by pointing, not prose.

Grounding data: convert tracks into points

For pointing, annotators find the frame where an object first appears, then click its exact location (frames at 2 fps). For curated data, existing segmentation masks become points by sampling a Gaussian around the mask center. For tracking, they repurpose Ref-VOS datasets and run SAM-2 to turn bounding boxes into masks, then mask-centers into point tracks.

The training mixture (Table 1)Interactive

Hover/tap a slice to see its sampling rate and example count. Note grounding (pointing + tracking) is deliberately up-weighted beyond its raw size — those tasks converge slowly.

Tap a wedge.
The deliberate imbalances tell a story. Video QA is downsampled ("video benchmarks converge quickly"). Video pointing is upsampled ("slow to converge"). Tracking is re-weighted to emphasize tail concepts (rare objects). Image pointing is downsampled because it was already seen in pre-training. Every sampling rate encodes a hard-won lesson about what the model struggles to learn.
How does Molmo2 build long-form QA data WITHOUT distilling from a proprietary model?

Chapter 8: Results — where open catches up

Three claims to verify against the tables. First, on standard video understanding, Molmo2-8B leads the open-weights-and-data field and challenges proprietary models on counting and captioning. Second, on grounding, it doesn't just lead open models — it beats Gemini 3 Pro on video pointing (38.4 vs 20.0 F1) and tracking (56.2 vs 41.1 J&F). Third, it's honest about its weakness: long (10+ min) videos, where it lags the best closed systems for lack of open long-video training data.

The headline grounding numbers (Table 3): On BURST video counting, Molmo2-8B hits 75.0 close-accuracy vs Qwen3-VL-8B's 74.4 and Gemini 3 Pro's 71.7. On video pointing F1, Molmo2-8B scores 38.4 — while Qwen3-VL-8B manages just 1.5 and Gemini 3 Pro 20.0. Open models simply couldn't point in video before this.

Two ablations that justify the tricks

The paper's training innovations aren't free decoration — bidirectional vision attention and token weighting each "improve performance" in ablations, and packing+message-trees deliver the 15× throughput that made training the full mixture feasible at all. The architecture is ordinary because the data and training discipline carry the weight.

Compare models below across the key axes. Toggle between the general video score, counting, and the grounding metrics where the open/closed gap actually inverts.

Molmo2 vs the fieldInteractive

Switch the metric. Watch how Molmo2 (warm) trails the best proprietary models on general understanding but leaps ahead on video pointing, where closed models collapse.

On which capability does Molmo2 most dramatically beat even proprietary models like Gemini 3 Pro?

Chapter 9: Connections & Cheat Sheet

Molmo2 sits at the confluence of three lineages: the ViT+connector+LLM design lineage (LLaVA, original Molmo), the pointing-as-text paradigm (PixMo, Molmo), and the open-data movement (OLMo, Tulu). Its move is to push pointing into time and to release everything.

How the pieces depend on each other

Points-as-text (Ch 1-2)
Makes grounding a generation task → needs no new head, but needs lots of grounding data.
9 new datasets (Ch 7)
Supplies that grounding data without distillation → but mixes wildly different output lengths.
Token weighting (Ch 6)
Balances the loss across those lengths → but the sequences are huge (16k+ tokens).
Packing + message trees (Ch 5)
Makes those huge sequences trainable (15×) → needs custom masks, hence Ulysses attention.
Bidirectional vision attn (Ch 4)
Squeezes free accuracy from those shared visual tokens within the custom masks.

The cheat sheet

Symbol / termMeaningValue / note
SVideo frame sampling rate2 fps
FMax frames per video128 (SFT) / 384 (long-context)
KMax image crops (tiling)8 train / 24 inference
(x, y, t, id)The point tokenx,y ∈ [0,100] normalized; t in seconds; id = object index
pool windowConnector spatial pooling2×2 images, 3×3 video frames
caption weightLoss weight for video captions0.1 (down-weighted)
pointing weightLoss weight for pointing0.2
other-task weightLength-balancing heuristic√(4n), n = answer tokens
packing gainExamples per 16,348-token seq~3.8 → ~15× throughput
vision attentionAmong visual tokensbidirectional (text stays causal)

Implement the point parser yourself

import re
# Parse Molmo2's point string back into structured tracks
def parse_points(text):
    # each point: t= id= x= y=
    pts = re.findall(r"t=([\d.]+) id=(\d+) x=([\d.]+) y=([\d.]+)", text)
    tracks = {}
    for t, oid, x, y in pts:
        tracks.setdefault(int(oid), []).append(
            (float(t), float(x), float(y)))     # [(t,x,y), ...] per id
    count = len(tracks)                                  # distinct ids = the count
    return tracks, count
# tracks[2] = [(0.5, 78.1, 55.4), (2.5, 79.0, 54.0)]  ← object 2's path

Related lessons

The mastery test. You should now be able to: write down the point token format and explain why x,y are normalized; derive why naively summing token losses over-weights captions and how √(4n) fixes it; draw the message-tree packed attention mask and explain each of its three rules; explain why vision tokens get bidirectional attention while text stays causal; and articulate why "no distillation" forced Molmo2 to build data instead of borrow it.
What is the single biggest reason Molmo2's contribution is the DATA and training recipe, not the architecture?