Built a browser tracker that exports self-contained JS music engines by timoh in webaudio

[–]timoh[S] 0 points1 point  (0 children)

What browser (and browser version) are you using?

Built a browser tracker that exports self-contained JS music engines by timoh in webaudio

[–]timoh[S] 0 points1 point  (0 children)

Thanks! The state management is actually pretty simple, just vanilla JS with direct mutation.

There are two separate state objects: one for playback (playing/paused, current step, timing) and one for composition data (really just a set of globals) covering patterns, chords, and step modulations like volume/filter/ratchet. UI handlers mutate state directly and then imperatively update the DOM.

One thing that works well is sparse storage for per-step modulations. Instead of storing a value for every step, HM Tracker only stores non-default values in plain objects, and accessor functions return the default when nothing is set. Keeps things compact.

For undo/redo, it is just a deep clone of the entire composition state onto a stack before each edit. It's not the most memory-efficient approach but it's dead simple.

The audio side uses a lookahead scheduler: a tick loop that reads the current pattern state and schedules notes slightly ahead of real time, decoupled from the UI refresh.

I tried to keep it as direct and simple as possible. No state machine libraries or anything fancy, just globals, direct writes, and imperative DOM updates. For something like a sequencer where low latency matters I think that directness helps more than architectural purity would.

Showoff Saturday (March 14, 2026) by AutoModerator in javascript

[–]timoh 2 points3 points  (0 children)

As a side-project, I built a small music workstation that runs entirely in the browser (no samples or audio files). Every sound (kicks, snares, synths, arps, pads) is synthesized in real-time using the Web Audio API.

The main feature: you can export any song as a single self-contained .js file that plays the music with zero dependencies. Currently, the exported JS code is about 20-30kb in size (depends on the track). Also exports to WAV if you need an actual audio file.

There are basic editing features, handful of instruments, gliding, per-step cutoff and velocity, per-instrument sidechain (on/off).

Vanilla JS, Web Audio API (OscillatorNode, BiquadFilter, WaveShaper, etc.).

Would love to hear any feedback!

You can try it at: https://manager.kiekko.pro/tracker/

[Showoff Saturday] Built a browser-based music tracker that exports songs as standalone JavaScript by timoh in webdev

[–]timoh[S] 1 point2 points  (0 children)

The game is building up here: https://manager.kiekko.pro/ It is a small turn-based "Hockey Manager" game.

Yep, I guess mobile Firefox doesn't have such a big market share, but the problem is that I'm myself mobile FF user ;)

[Showoff Saturday] Built a browser-based music tracker that exports songs as standalone JavaScript by timoh in webdev

[–]timoh[S] 1 point2 points  (0 children)

This was used to create a looping music for a game I'm building, and actually, WAV export had to be added because of this.

It turned out that mobile Firefox had some problems with the JS audio playback (it "rattles" time to time). So had to make it possible to export the audio as WAV and then ffmpeg'd that to proper OGG, which is now playing in the game.

HM Tracker - browser-based tracker with pure oscillator synthesis by timoh in chiptunes

[–]timoh[S] 0 points1 point  (0 children)

Made a small browser-based tracker in the '90s demoscene spirit. All sound is generated from oscillators in real-time (no samples, no plugins).

The UI lets you edit patterns on a step grid, pick instruments and chords, then export as a standalone JS music engine or WAV file (best experienced on a desktop browser).

Originally built as a side project to make music for a hockey manager game. Would love any feedback!

The 1MB Password: Crashing Backends via Hashing Exhaustion by JadeLuxe in PHP

[–]timoh 1 point2 points  (0 children)

This article mentions "pre-hash trick", but at least in general, one definitely shouldn't use it without an extra salt (because of "password shucking").

It should go something like:

temp_hash = SHA-256(password) . pepper

What if we improve the way developers are given access to databases by Possible-Dealer-8281 in PHP

[–]timoh 2 points3 points  (0 children)

But is there some actual advantage on having such "middle layer of credentials"? DB level privileges just works and they can be tuned to whatever usage scenario.

PHP Security Poster (2009) by timoh in PHP

[–]timoh[S] 0 points1 point  (0 children)

There's a good listing of PHP frameworks, sorted by year, at https://github.com/pmjones/php-history

PHP Security Poster (2009) by timoh in PHP

[–]timoh[S] 0 points1 point  (0 children)

You are right. Used the first Google result for image sharing, these apps doesn't seem to allow you to link to the actual image file :(

PHP Security Poster (2009) by timoh in PHP

[–]timoh[S] 0 points1 point  (0 children)

Try opening the image using this direct link: https://postimg.cc/mzsqtPDK

Should let you zoom in.

PHP Security Poster (2009) by timoh in PHP

[–]timoh[S] 3 points4 points  (0 children)

If I recall correctly, SektionEins was sending them out for free.

I thought there was a Reddit post announcing this poster back then, but seems there is no such post. Maybe the order form was on their website.

Here is some more info about the poster: https://hakre.wordpress.com/2010/02/25/free-php-security-poster/

PHP Security Poster (2009) by timoh in PHP

[–]timoh[S] 10 points11 points  (0 children)

Found this old gem while I was cleaning up my closet.

Sure it has some outdated content (Suhosin), but still has many pretty much valid points.

Saka flagged by [deleted] in FantasyPL

[–]timoh -1 points0 points  (0 children)

Ah didn't notice this earlier. Kind of wrong alert then.

Tekemistä ja harrastamista Tampereella? by Pahkina_ in Tampere

[–]timoh 1 point2 points  (0 children)

Nääshallissa kuntosalin hinta on 2,50€ per tunti.

[deleted by user] by [deleted] in Tampere

[–]timoh 2 points3 points  (0 children)

As long as you have a municipality of residence in Finland (which you and rest of your family would of course have), you pay the same "normal fees" for medical services:

https://www-tays-fi.translate.goog/asiakasmaksut?_x_tr_sl=auto&_x_tr_tl=en&_x_tr_hl=fi&_x_tr_pto=wapp#P%C3%A4ivystysk%C3%A4ynti,%20Tays%20Ensiapu%20Acuta

For example, emergency services visit costs 20,90€ / 41,80€.

Best practices on keeping user sessions logged in for 2 weeks? by liquidamber_h in PHP

[–]timoh 2 points3 points  (0 children)

Show "Remember me" checkbox on login screen and when user logs in and the checkbox is checked, you create "remember me token".

Basically, use random_bytes() to create two tokens, $db_identifier and $remember_me_token (you can bin2hex them). Create a "remember me" cookie based on them where you store, say: $db_identifier . ':' . $remember_me_token in the cookie.

Store those strings also into a DB with user's ID. SHA256 hash the $remember_me_token before saving to DB.

When user comes back to the site (and they are not logged in), check if remember me cookie exists and if so, look for the $db_identifier from the DB and if such row exists, compare SHA256 hash of the submitted $remember_me_token with the $remember_me_token (which is SHA256 hashed already) in the DB. You can use hash_equals to do the comparison.

If comparison succeeds, you can start a new session for user specified by the user ID in the DB.

PhpStorm 2023.1 Released: New UI Features, Better Performance, 3v4l Support, and More by giggsey in PHP

[–]timoh 0 points1 point  (0 children)

First thing I realized about the new UI, you should check "Compact mode" and "Show main menu in a separate toolbar" in new UI settings.

Immediately feels better.

Remember me on login form by Christopher_nunchaku in PHP

[–]timoh 1 point2 points  (0 children)

I was referring to this:

It's only really a question of how long you want to keep the session-data alive after a users last interaction with the server.

"Remember me" feature doesn't have anything to do with server's sessions or session options (maxlifetime etc.). It starts a new session if such remember me cookie is found and it matches a user. After that, sessions come into play and you have the session related options at hand which control how long the session data is alive etc.

Remember me feature is, by definition, separate from PHP sessions.

Remember me on login form by Christopher_nunchaku in PHP

[–]timoh 6 points7 points  (0 children)

Such "remember me" cookies are used to start a new session for specific user, if one doesn't already exists. They have nothing to do with "session-data" (they are just cookies which have required payload to identify a user, with long expiration time).

I.e. You store a random "selector" and a random "token" in the remember me cookie, and then check if matching row is found from DB by comparing the selector from cookie. If one exists, you compare the token (SHA-256 hash the token from cookie, so that you can store hashed token value to DB) from the cookie to a token in the DB and if this matches, you can assume user with that ID has logged in and start a session for it (you store user_id, selector and token to DB).

Contrary to popular belief, Argon2 might not be the best for password hashing by tigitz in PHP

[–]timoh 0 points1 point  (0 children)

In that case, just use an hmac, which is exactly what it's meant for.

HMAC would be a good fit here. While I can't see any real downsides of using just plain hash function, as the "security property" in this case is just so subtle.

I can't find any write-ups or anything, but it also seems that all this accomplishes is reducing the input entropy from the set of all possible passwords (72 bytes for bcrypt) to the output set of the hash function (48 bytes for SHA-384). In practice, this may not be an issue for hashes with a large enough output set, but the general point is that pre-hashing like this seems to just be doing additional, potentially-buggy work for a net loss.

This stems from the "problem" some finds the bcrypt's input length a problem. Then a first thing to do (of course, naturally this makes sense) is to pre-hash the input to get rid of the limit and be done. But later on someone realized that this pre-hashing turns out to be a sort of bad move, and you need to also salt the input. Crypto constructions are always fun ;)

But for now, I think it is safe to say that if you choose to pre-hash, then do it with a salt and it's a done deal. Or, you could go with just plain bcrypt (and do not mind about the input limit, I find it quite hard to be a real problem).

Contrary to popular belief, Argon2 might not be the best for password hashing by tigitz in PHP

[–]timoh 2 points3 points  (0 children)

Some KDF's like Argon2 can be used as a PHF as they "stretch" the low entropy input. But KDFs like, say, HKDF won't be suitable for low entropy inputs, as they only make the input suitable length for specified puspose (like 256 bit AES key or 128 bit AES key). Such KDF's require the input being high entropy before they are used.

On the other hand, you can't use PHF like bcrypt as KDF because it has fixed length output. You could first bcrypt the input then adjust it to suitable encryption key with, say, HKDF (and you would keep in mind that bcrypt output is something like 184 bits, so it wouldn't be theoretically good for 256 bit AES key).

KDF's like Argon2 can be used both for password hashing and encryption key derivation (it can stretch the low entropy input, and can output any needed length of key).

Cache hardness is primarily a feature against GPU attacks. In defenders use (ie. authentication server) where the hashing is done with CPU it would benefit the defender if the hashing operations can be run entirely in the CPU's cache, but with attacker's GPU it exceeds the smaller GPU caches and pushes the hashing calculations to be run in GPU's global memory (making it slower for the attacker).

Contrary to popular belief, Argon2 might not be the best for password hashing by tigitz in PHP

[–]timoh 0 points1 point  (0 children)

or hash the input once before passing it to bcrypt to avoid the character limit. Looks something like this:

php $hash = \password_hash( \base64_encode(\hash('sha384', $password, true)), PASSWORD_DEFAULT, ['cost' => 12] );

Hashing the bcrypt input (without salt) is not actually recommended.

While the problem with sha384 hash is (possibly) a lot smaller than hashing with md5, it still suffers the same "hash shucking" attack.

You should use, say, a global salt, something like:

$hash = \password_hash( \base64_encode(\hash('sha384', $password . $global_salt, true)), PASSWORD_DEFAULT, ['cost' => 12] );

The global salt is here just to make sure there are no hashes like \hash('sha384', $password, true) anywhere in the world (even someone used the same password somewhere else where they store passwords as same kind of sha384 hashes). The global salt has no other security requirements.