Notch trying jsFiddle
jsfiddle.net
jsfiddle.net
And indeed it is pretty impressive how fast the rendering happens given that the code operates at such lower level... Even selecting the color of every pixel requires non trivial work in the inner loop.
AWESOME code.
low level = close to hardware, high level = high abstraction, like JS
Javascript is high level, but here notch is using it as a low level framebuffer.
Sorry if this is a stupid question, but wouldn't you normally expect low level code, whilst more time consuming to write, be faster or just as fast as higher level code.
For example, how C is used as a standard for fast code since it's so low level.
However if you use this approach in Javascript you get poor performances unfortunately. And indeed this demo requires fast computers to do what otherwise could be done in C with direct access to the video frame buffer with maybe 50 times slower computers.
Even C coders almost no longer try to do this low level things and let the graphic chip do a lot of the work when the goal is to produce a game.
So in Javascript instead you want to usually call a library that implements a 3D engine in a lower level language like C and with more direct hardware access.
But in this case, it makes sense to do all this in a low level way and in Javascript: it is just a demo that is full of interesting code and with an appealing visual output.
In this case, Notch is writing code that doesn't necessitate the overhead of using JS. It could easily be rewritten as C and probably run faster that way. Not that it isn't interesting, of course.
But he's actually using Javascript arrays which can get passed around inside a javascript VM, and do not have a set size. Not very "low level".
I spent a moment and changed the arrays to Float32Arrays which have set sizes and sit in memory without being fucked with by the VM and sped the demo up dramatically.
"Code is awful due to the nature of the project"
All this using nothing more than a frame buffer, so you could port this easily from a C64 to any other computer. This code contains a lot of knowledge VS use of pre-build APIs.
If a programmer reads this code and understands how every part works, he or she ends knowing a lot more about computer graphics than before.
It contains a lot of general knowledge about 3D graphics, and absolutely 0 specific knowledge about the environment in which it's running. All he's accomplished by doing it this way is show off that he knows trig and prevent any of it from being hardware accelerated.
>If a programmer reads this code and understands how every part works
But he never will, because the code is illegible.
The sad part, though, is it doesn't particularly scale well with canvas (yet) and using sprites, shape primitives, or even WebGL is way to get full speed at a regular resolution. (Imagine a 640x480 pixel field of randomized colors, you can get hundreds of FPS for that in Java without a sweat. Not so in the browser and most certainly not in JRuby.. my PotC port struggled to hit 12fps with equivalent code.)
The title of "ChamberedTest" here makes me wonder if notch is planning to use JS and canvas in the forthcoming Ludum Dare 25 (the gamedev contest where he created Prelude of the Chambered). I hope so!
> The sad part, though, is it doesn't particularly scale well with canvas (yet) and using sprites, shape primitives, or even WebGL is way to get full speed at a regular resolution.
It doesn't just not scale well with canvas, it doesn't scale well with modern hardware. A single call to draw a textured polygon of any size is ~as expensive as the call drawing a single pixel. A call to draw a stored model with with thousands of polygons is ~as expensive as the call to draw a single polygon.
When doing pixel at a time drawing like that you are completely ignoring the specialized hardware found in the GPU, which is vastly more efficient at pushing pixels than the CPU, even in the lowest-end Intel integrated GPUs of the newest generation.
If you want to do serious graphics, you really should just get used to OpenGL/DirectX.
It looks to me like it's probably making a single call for each frame. Perhaps it mostly depends on how well the pixels.data[] call is implemented.
Spent most of today learning new stuff. Ported Minecraft4k. Code is awful due to the nature of the project, but here: http://jsdo.it/notch/dB1E
ctx = document.getElementById('game').getContext('2d');
pixels = ctx.createImageData(w, h);
ctx.putImageData(pixels, 0, 0);
EDIT: Thanks for the clarification!
He builds the raster image pixel by pixel instead of using higher level primitives (polygons).
it'd be also great to do an analysis of the geometry data as well, if you can!
http://www.reddit.com/r/programming/comments/146v69/how_notc...
Please do NOT write code like this. It was originally written for the Java4k
competition, which focuses on executable code size, and is as a result almost
intentionally poorly written. It got even worse when I ported it to JS.
It was mainly meant as a test for me to see what HTML5/JS can do, and an
exercise in porting code over.
http://www.reddit.com/r/programming/comments/146v69/how_notc... It would run a bit smoother if it was written in C++ (mainly due to not having
to rely on the GC, and having it precompiled gets rid of the warmup time), and
modern OpenGL would help quite a lot as well. A lot of the stuff done in CPU
now could be moved to a shader, which both makes code simpler, and gets rid of
the slow JNI calls required now.
The main reason why Minecraft is slow is mainly 1) There's a LOT of polygons,
combined with 2) The difficulty of making an efficient culling algorithm in a
dynamic world.
If someone solves #2, very interesting things could be made with a game similar
to Minecraft.
http://www.reddit.com/r/programming/comments/146v69/how_notc... No, they don't get merged because the textures are all pulled from the same atlas
to avoid texture swapping. With GLSL, they could be merged, saving quite a lot of
polygons. For a while, I did attempt switching the atlas from a 16 x 16 grid to a
1 x 256 grid and merging horizontal runs of the same texture, but the resulting
texture size was to tall some graphics cards (on low end computers) would
automatically downsample it.
The problem with the occlusion culling is not about knowing what parts are static,
but rather figuring out what occluders there are. It would be very beneficial not
to have to render caves under ground below the player, for example, or not to
render the entire outside when a player is inside a small closed house. Figuring
this out in runtime on the fly as the player moves around is.. expensive.Incidentally, Bret Victor's talk was the starting point for Khan Academy's CS curriculum that John Resign led (http://ejohn.org/blog/introducing-khan-cs/).
Edit: I managed to get this running in livecoding.io, which does some of what Bret Victor was talking about (basic sliders and color pickers): http://livecoding.io/4191583. Not sure why it's running so much slower though...
the problem is from calling setInterval every time the code is evaluated, leading to way too many function calls/second
for ( var i = 1; i < 16; i++) {
var br = 255 - ((Math.random() * 96) | 0);
for ( var y = 0; y < 16 * 3; y++) {
for ( var x = 0; x < 16; x++) {
...
}
}
}
If this was a first introduction to programming it would scare off many people.My interpretation of Bret's idea is that it would be better to have the ability to highlight the section of the texture being written by each section of the loop. It's not the sliders & live editing that are most important it's about linking abstract control into meaning within the learner.
Press play in the bottom left to have it go, and scrub numbers/colors to your hearts content!
Part of the reason it slows down in livecoding is because of the way the original code uses setInterval, and every change reevaluates the code which polutes the scope and starts way too many threads going. I've added optional functionality to Tributary which gives you a run loop (using requestAnimationFrame) that doesn't polute anything.
Hope this helps!
Then a random map is created, with the "center" that is set to empty blocks with high probability. Finally a function traces the map on the screen at every tick of the clock.
for ( var x = 0; x < w; x++) {
var ___xd = (x - w / 2) / h;
for ( var y = 0; y < h; y++) {
var __yd = (y - h / 2) / h;
var __zd = 1;
var ___zd = __zd * yCos + __yd * ySin;
var _yd = __yd * yCos - __zd * ySin;
var _xd = ___xd * xCos + ___zd * xSin;
var _zd = ___zd * xCos - ___xd * xSin;Part of me can't decide if that's awesome or tragic.
As you may have anticipated, this demo runs at less than 1 frame per second on my Nexus 7. :/
In terms of per-pixel manipulation of a Canvas, it is very slow, even with javascript's (not very well supported) typed arrays. Simply put, multidimensional loops (unrolled or not) will always kill performance in JS with any significant dimensions. I learned this the hard way by trying to write a pixel-based GUI, and even that (with some crazy optimizations) could not render fast enough for all devices. If you are just doing blits, Canvas works very well (especially since this operation often uses the GPU).
For now, I think the best option is still WebGL, even though it is not widely supported yet, mobile devices are beginning to pick it up (Blackberry, for example).
What I meant to say is that it is using Canvas 2D, instead of Canvas 3D, also known as WebGl. Antirez gave a wonderful explanation above[1]:
And indeed it is pretty impressive how fast the rendering happens given that the code operates at such lower level... Even selecting the color of every pixel requires non trivial work in the inner loop
[1] Of course, it gets more complicated if you want to be more realistic, like including a field of view (cone) rather than a screen-sized chunk (prism) of 3D objects, and so on, but these are just details.
What I'm seeing in this demo is 3D in every sense, the code is basically a very simple 3D engine, and it requires knowledge of computer graphics to understand it, so the top-level commenter here asked a valid question.
The next set of three nested loops is generating a random "world" with a tunnel cut out of it.
The renderMinecraft function is performing a minimal perspective projection http://en.wikipedia.org/wiki/3D_projection for a single ray cast into the world. Each pixel is cast and then, for each object hit by the ray cast, the closest is found, and a texture mapped pixel is calculated and written into the frame buffer.
It is pretty cool to see how a trivial lightning model like that can produce a pretty looking result.
If you want to see just the brightness of faces without texture to see more easily how light is used, just change the line:
var cc = texmap[u + v * 16 + tex * 256 * 3];
Into: var cc = 255+(255<<8)+(255<<16);
To also remove the "distant is less bright" effect just add: ddist = 255;
Before: var r = ((col >> 16) ...using window.requestAnimationFrame just because we can!
setInterval(clock, 1000 / 100);
Is there any reason for writing 1000/100 instead of 10?edit: Thanks guys!
The usefulness is more obvious when you want 60FPS, which is not easily represented without rounding.
Similarly, people will write (10 * 60 * 60) if the value is seconds and the programmer intends to indicate 10 hours.
The "cool" thing about this demo, was that Canvas was not widespread back then so I generated all the chunky effects on the fly as 24bits BMP image, then made a data: URI and update an IMG tag.
Doesn't any web browser on Android use a JIT?
http://blog.nsbasic.com/?p=1060
Runs fine on an iPhone 5!
This demo has a little bit of minecraft in it. :)
It rendered it fine, but I could tell that the DOM was being abused heavily. I don't mean that in a bad way, but it just felt very 'heavy'.
On the flip side, this is awesome that the browser can do this - and it's awesome that browsers can do this.
One positive effect of doing this is that the code is _very_ portable and doesn't require any crazy feature in the browser or the PC (for example, i'm able to see the demo on my work computer even though WebGL doesn't work on it because of lack of drivers).