Songs are saved on this device first. Open the Local tab and press Upload online to send one to staff for review.
Enter the access code to use Rhythmhel.

A theme is just a short list of colors. Rhythmhel reads them the moment you leave the box, saves them on this device, and repaints everything, including the playfield.
--accent:#ff66aa;. Wrapping everything in :root{ ... } is optional.#12101a. Names and rgb() work too, but transparency is ignored./* text between these */ is a comment and is ignored.| --bg | Main background behind everything |
| --accent | Highlights: selected items, buttons, header underline, lane rails |
| --seltext | Text drawn on top of the accent color. Keep it readable against --accent |
| --panel | Cards, song rows and buttons |
| --panel2 | Header bars, side panels and text boxes |
| --edge | Borders and divider lines |
| --text / --dim | Normal text / quieter secondary text |
| --lane | Playfield background |
| --line | The hit line |
| --lane-1 to --lane-4 | Note colors for 4K, left to right (2K uses lanes 2 and 3, 3K uses 1 to 3) |
| --lane-solo | Note color in 1K |
| --perfect / --great | Judgement colors for good hits |
| --miss / --bad | Miss color / mash and fake-note warning color |
| --bg-dim | 0 to 0.9, how dark song backgrounds are. The Background dim option overrides it if you set one |
| --trail | Optional. Mouse trail color. If you leave it out, the trail is built from --accent and automatically adjusted so it stays visible on your background |
| --logo | Optional. Swaps the Rhythmhel logo (main menu hexagon, header bar, help windows). Use url("https://...") or a data: URL. Default is rhythmhel.png |
| color-scheme | Set to light for a light theme so form controls match |
--panel a little lighter than --bg, and --panel2 a little darker.color-scheme:light;, a dark --text, a dark --line, and --seltext:#ffffff..css file with Load a .css theme file, and share the file with friends.:root{
/* main colors */
--bg:#0c0d1f; /* background */
--accent:#a8a8e8; /* highlights and buttons */
--seltext:#0c0d26; /* text on the accent color */
--panel:#15173a; /* cards and buttons */
--panel2:#0e1029; /* bars, side panels, text boxes */
--edge:#3b3f78; /* borders and dividers */
--text:#eff0ff; /* normal text */
--dim:#9b9fcf; /* quiet text */
/* gameplay */
--lane:#080913; /* playfield */
--line:#ffffff; /* hit line */
--lane-1:#a8a8e8; /* 4K notes, left to right */
--lane-2:#e6b4ff;
--lane-3:#8fdcff;
--lane-4:#fff0b8;
--lane-solo:#e6b4ff; /* 1K notes */
--perfect:#c8c8f8;
--great:#b8f0d0;
--miss:#d05a5a;
--bad:#ff6a86;
--bg-dim:.6; /* song background darkness, 0 to 0.9 */
/* --logo:url("rhythmhel.png"); optional: swap the logo image */
/* --trail:#a8a8e8; optional: override the automatic mouse trail color */
}
Pick a key count and difficulty. A chart with the same name is replaced and its leaderboard is cleared.
Only the uploader (or staff) can export a song's charts.

A mod is one JavaScript file. Rhythmhel runs it with a single variable called rh. Mods are saved on this device and can be switched off or removed in Options, Mods.
A mod runs with the same power as the game itself: it can read your settings and your login session. Only install mods from people you trust, and read the code first. While any mod is enabled, your scores are not uploaded to the leaderboards.
counter.js (example below).// counter.js: shows a Perfect counter while playing
const size = rh.option({id:'size', label:'Counter size', type:'number', def:22, min:12, max:60});
rh.on('frame', screen => {
if (screen !== 'play') return;
rh.tx('Perfects ' + rh.play.c[0], 24, 150, size(), rh.theme.perfect);
});
rh.on('judge', j => { if (j.rating === 'Miss') rh.toast('Missed on lane ' + (j.lane + 1)); });
rh.menu('Hello', () => rh.toast('Hello from my mod!'));
rh.css(':root{--accent:#ff66aa}'); rh.refreshTheme();
| frame | Every frame after the screen is drawn. Gets the screen name: menu, songs, charts, lb, play, results, mp, room, edit, msg. Draw on rh.g |
| judge | A note was judged: {rating:'Perfect'|'Great'|'Miss', lane, time, combo, score} |
| start | A chart started. Gets the live play state |
| finish | A chart ended. Gets the results |
| key | Any key press (a KeyboardEvent) |
| option | A mod setting changed: {mod, id, value} |
| rh.menu(label, fn) | Adds an item to the main menu |
| rh.option({id,label,type,def,...}) | Adds a setting under Options, Mods. type: number, bool, text or select (with options:[...]). Returns a function that gives the current value |
| rh.store.get(k, def) / set(k, v) | Saves your own data on this device |
| rh.css(text), rh.refreshTheme() | Adds CSS. Use refreshTheme after changing color variables |
| rh.toast(text) | Small message at the top right |
| rh.go(screen) | Jump to menu, songs or mp |
| rh.tx, rh.rr, rh.btn, rh.panel, rh.hexP, rh.logo | The game's own drawing helpers: tx(text,x,y,size,color,align,maxWidth), rr(x,y,w,h,radius,fill,stroke), btn(x,y,w,h,label,fn,highlight), logo(x,y,width) |
| rh.g, rh.W, rh.H | Canvas context and its size. The height is always 720 units, the width follows the window |
| rh.cfg, rh.theme | Settings and theme colors (accent, text, lanes...) |
| rh.screen, rh.song, rh.user, rh.t | Current screen, selected song, player name, seconds since load |
| rh.play | Live play state while playing: score, combo, max, c ([perfect, great, miss]), m (mashes), K, notes. Otherwise null |
rh.play.score and expect it to be accepted: the server recomputes every score from your key presses.Change each key separately: shape, color and image. It applies to that key's notes and its key button. “Default” follows your global note shape and the theme's lane color.
OptionsTune Rhythmhel to your liking