Show HN: I've re-implemented the Doom fire effect in plain JavaScript
github.comThe original reverse-engineering done by Samuel Villarreal was already using javascript. I assume by "plain" you mean you removed easeljs library for canvas management and added buttons?
Yes Fabien! Also thanks for your article, it really inspired me to code my own version. Had a great time! I'm planning to make a video about all this and I will tweet you when it's published.
Maybe it wasn't clear in the original article [1], but what you see inline are javascript implementations of the effect - there is a link to the source code at the end:
+ link to GitHub
https://github.com/fabiensanglard/DoomFirePSX/blob/master/fl...
The blog post is sadly not easy to follow along to try to do an implementation due to what appear to be minor description mistakes or poor writing which confuses.
It switches from saying that 0 in the map represents white, to saying it is black, and then in the loop it states that the first row isn't changed but the loop pseudo-code skips over the first row only to then overwrite that first row in the code:
> firePixels[src - FIRE_WIDTH] = firePixels[src] - 1;
The comment, "Notice that line 0 is not updated" doesn't make clear it's not talking about the start of the buffer: It clearly changes index 0. In fact if line 0 is the 'generator' row it is in fact the last line in the buffer and the description of why it doesn't change is wrong: It's not unchanged because the loop starts at 1, it's unchanged because the loop is modifying 'src-FIRE_WIDTH'.
Maybe some of the description or pseudo-code described are describing two different implementations one of which maybe is working with a reversed buffer.
The principle however is clear, and it is interesting to see in the browser, although I'm sticking to just loading it into a canvas directly with CanvasRenderingContext2D.putImageData which lets you directly write an image buffer (noting that it is RGBA so 4 bytes per drawn pixel).
In fact the 'cleaned up' version linked at the bottom appears to do the same.
I've addressed those semantic issues in the version I wrote. I also got very confused by the article explanation.
I first learned to do this effect in 1999 thanks to the incredible Denthor/Asphexia tutorials. I looked forward to new releases of these that my friend would (somehow) get and give to me on a floppy disk.
Most of them were in Pascal, but the fire one was in asm (http://archive.gamedev.net/archive/reference/articles/articl...) - it was the first time that assembler really clicked for me!
Oh wow. Denthor's tutorials were very good. Brings lots of good memories. Too bad this culture was overrun by internet.
I remember those! Incredibly accessible. Also remember writing Den(e?)thor as an insecure, new-to-the-internet 16-year old asking whether more tutorials were in production. Never received an answer, unfortunately.
Wonder what he (?) is doing today.
Oh yes, brings back memories. For us older folks who'd like to revisit those tutorials here's the archive: http://archive.gamedev.net/archive/reference/listed82.html?c...
I remember this tutorial's intro via ASM to help solve a challenge of making fire run the same on a 386 sx16 as a 486 dx2/66, a big jump at the time.
Still a little humorous that optimizing fire is still a topic that gathers attention and optimization despite the increase in computing power.
It's a bit slow. I suggest 1) Use existing table instead of creating a new table at each frame/interval. 2) Or use Canvas because DOM is so slow 3) requestAnimationFrame instead of setInterval
Open Source community is awesome, someone already made a Pull Request about this: https://github.com/filipedeschamps/doom-fire-algorithm/pull/...
For canvas I’d be curious which would be faster: grabbing an ImageData and continually update then set or use fillRect.
You always get surprises when optimizing, for example IE11 and probably other browsers use DOM optimization with hardware acceleration, so keeping it as a table, and just changing the bgColor instead of creating a new table, might be fast, fast enough, or even faster then canvas. As for imageData vs fillRect I would go with fillRect first as it makes it easier to scale to the clients pixelRatio, eg you want to scale to 200% on a device that has a pixel ratio of 2 in order to make it look crisp. In my experience, manipulating single pixels are sometimes also slower! But if you really want to get advanced you could divide the screen up in different pieces and use many (off-screen) canvas's and/or web workers, but it would probably only work on Chrome, and then you would operate on the imageData buffer.
Yeah it’d probably require implementing all approaches and benchmarking in various browsers.
For scaling I find using CSS works well instead of scaling the canvas. Most browsers support `image-rendering: pixelated` now. Of course no MS browser does.
I’ve always been impressed with canvas. I recently wrote a little app that creates 1024 16x16 canvases and I’ve yet to find any browser (including mobile) break a sweat.
Apple ][ Fire Demo -- 64 Bytes http://deater.net/weave/vmwprod/apple2_fire/
But can you implement JavaScript in Doom?
Would you settle for Minecraft? That's known to be Turing-complete.
Y'know, I wouldn't be surprised.
Didn't MS VC++ 4 to 6 ship with an example which emulated this effect as well via a custom control?
There were different tabs for things like "wood", "gas", "cryptonite" which changed the overall colour of the effect.
It would be really cool if you could paste the url to an image, have a generator overlay the fire effect on top, and you can save that as a gif.