Local LLM Lab

Start a comparison

Hextris

Play Hextris once while up to three scoring paths watch the same play: frames through Gemini in the cloud (today's pipeline), a text digest of the game state through a model running in this browser, or the same digest through Gemini. Each path has its own settings and its own session, so two browser models can be compared on identical input.

Arrow keys rotate the hexagon; P pauses. Press Start in the game to begin.

Scoring paths · 2 of 3
Vision · frames → Gemini
Connectionapi.staging.skillprint.coEdit
Local · Qwen2.5-1.5B · v2adjusts the game
Connectionapi.staging.skillprint.coEdit

Checking what this browser can run…

What browser_webllm is given (v2) · system ~361 tok + user ~128 tok → 6 judgments back
input 2 messages, both re-read by the model on every chunkdigest ~122 tok (compact) built from a sample 5 s chunk of Hextrismeasured before the model 34 leaves (never asked of it)asked of the model attention_focus, cognitive_load, emotional_valence, arousal_level, difficulty_match, mood, overviewoutput ~45 tok (cap 96), constrained to the JSON schema below

System message (identical for every chunk)

You score one 5-second chunk of a game session from an exact SDK digest; you never see pixels.
Game: Hextris. Blocks fall onto a hexagon the player rotates left/right; 3+ touching same-colour blocks clear and score; quick clears chain a combo; a side stacking past the edge ends the run; spawns speed up as difficulty rises.
Reply with one JSON object. Scores are integers 0-100 unless noted.
attention_focus: continuity of engagement. Active 100% of seconds, longest idle under 1s, visible 100% = 85-95. Half the seconds idle = 40-60. No inputs = 5.
cognitive_load: thinking demand. Falling 4+ or difficulty over 20 = 80+. Difficulty under 8 with falling 2 or fewer = 30-50. Menu, paused or game-over with no inputs = 5.
emotional_valence: -100 frustrated to 100 delighted. GAME_OVER in events = -60 or lower. MATCH with best combo x3 or more, or score above personal best = 50 or higher. Neither = -10 to 20.
arousal_level: pace. 15+ inputs in 5s or difficulty over 20 = 80+. Under 5 inputs in 5s = 20 or lower.
difficulty_match: 50 = the challenge fits. Hit rate 100%, no GAME_OVER, longest idle over 1s = 20-35 (too easy). Hit rate under 50% or GAME_OVER = 75-95 (too hard). Hit rate 60-99% with no GAME_OVER = 45-60.
mood: one of relax, focus, grit, joy, curiosity. relax = slow and easy; focus = steady accurate play; grit = struggling or just lost; joy = combos with energy; curiosity = changing approach.
overview: under 80 characters.

User message for a sample chunk (target, the compact digest, the cue for JSON)

Target mood: focus
Hextris chunk 14, 5.0s
start: score 1840 combo x3 sides=[2,1,3,1,2,2] falling 2 locked 11 difficulty 6.4 spawnEvery 1850ms playing
end: score 1960 (+120, best 1240) combo x0 sides=[3,2,3,2,2,2] falling 3 locked 14
events: SPAWN x3, LAND x3, MATCH x2 (best run 4, best combo x4), ROTATE x11
inputs: 11 in 5.0s, median gap 0.42s, cv 0.15, longest idle 0.6s, active 100% of seconds
reaction 0.30s (n=3), placement 0.28s, hit rate 100%
visible 100%, 60fps
last 3 chunks: flow 0.71, 0.74, 0.69
JSON:

How to read a digest

chunk / span
the chunk's index and its length; the target is on the user message's first line instead
start
the first state sample: score, combo, blocks per side, falling and locked blocks, difficulty, spawn interval, game state
end
the last sample, carrying only what changed since the start, plus the score gained this chunk and the personal best
events
counts of SPAWN, LAND, MATCH (with best run and combo), ROTATE, GAME_OVER and RESTART
inputs
how many key or pointer inputs, the median gap between them, their coefficient of variation, the longest idle stretch, and the share of seconds with any input
reaction / placement / hit rate
median spawn-to-rotate latency, median last-rotate-to-landing time, and blocks that formed a match over blocks landed
visible / fps
the share of the chunk the tab was visible and the frame rate; long frames only when there were any
last N chunks
recent flow scores, so the model has a trend

Response constraint (JSON schema the backend enforces where it can; integers are divided by 100 before scoring)

{
  "type": "object",
  "properties": {
    "attention_focus": {
      "type": "integer",
      "minimum": 0,
      "maximum": 100
    },
    "cognitive_load": {
      "type": "integer",
      "minimum": 0,
      "maximum": 100
    },
    "emotional_valence": {
      "type": "integer",
      "minimum": -100,
      "maximum": 100
    },
    "arousal_level": {
      "type": "integer",
      "minimum": 0,
      "maximum": 100
    },
    "difficulty_match": {
      "type": "integer",
      "minimum": 0,
      "maximum": 100
    },
    "mood": {
      "type": "string",
      "enum": [
        "relax",
        "focus",
        "grit",
        "joy",
        "curiosity"
      ]
    },
    "overview": {
      "type": "string",
      "maxLength": 80
    }
  },
  "required": [
    "attention_focus",
    "cognitive_load",
    "emotional_valence",
    "arousal_level",
    "difficulty_match",
    "mood",
    "overview"
  ],
  "additionalProperties": false
}

Measured in the page from the same sample, tagged by source, and merged with the model's answer into the indicator payload

game_progress = 1.000  [measured]
input_consistency = 0.853  [measured]
response_speed = 0.797  [measured]
exploration_level = 0.550  [measured]
technical_issues = 0.000  [measured]
environmental_distractions = 0.000  [measured]
social_engagement = 0.000  [default]
gaze_intensity = 1.000  [proxy]
physical_tension = 0.550  [proxy]
focused_elements = 3  [measured]
distractors_present = 0  [measured]
pattern_completion_state = 1  [measured]
pattern_complexity = 1.829  [measured]
active_patterns = ["color_match","spatial_alignment"]  [measured]
path_preparation = true  [measured]
next_objective_visible = true  [measured]
resources = [{"resource_type":"combo_time","quantity":0,"usage_rate":60},{"resource_type":"headroom_rows","quantity":5,"usage_rate":0.6}]  [measured]
stimulus_present = true  [measured]
response_required = true  [measured]
stimulus_type = "visual"  [measured]
precision = 1  [measured]
timing_accuracy = 0.863  [measured]
movement_complexity = 2  [default]
solution_progress = 1  [measured]
attempts_made = 0  [measured]
strategy_changes = 0  [measured]
recall_accuracy = 0  [default]
sequence_length = 0  [default]
elements_to_remember = 0  [default]
depth_perception = 0.500  [default]
navigation_efficiency = 0.467  [measured]
object_tracking = 3  [measured]
current_action = "key:ArrowRight"  [measured]
interaction_objects = ["hexagon","falling_block"]  [measured]
Local model consolebrowser_webllm · Qwen2.5-1.5B-Instruct-q4f16_1-MLCcold
0 lines
Nothing yet. The model logs its warm-up here, then each chunk: the digest it read, the judgment it made, the post to the API, and the API's score.
Every judged chunk and API score is kept in this browser for offline replay of prompt and model changes.

Local · Qwen2.5-1.5B · v2: model not loaded yet. Load it now, or the first chunks will wait for the download.