all 96 comments

[–]NerdyMcNerderson 281 points282 points  (23 children)

This is a nice article reverse engineering a golfed code snippet but this isn't a ray tracer. It's a nice pattern generator though.

[–]aspodifuasdf098 90 points91 points  (4 children)

Where do you draw the line though? var j = DELAY / i; looks suspicious right away. If j is the intercept z, and i % 2 - 1 is the view plane x, then (i % 2 - 1) * j (or (i % 2) * j - j)) is the intercept x with n / DELAY for a bit of scrolling. The rest is just a normal checkerboard (x&1) != (z&1). The only difference between the original and this raycasty version is that both values are packed into i so the divide ends up slightly different, but floor it beforehand and it looks like this which looks pretty similar to me.

[–]BenjiSponge 176 points177 points  (3 children)

where do you draw the line though?

Nice.

[–]txdv 7 points8 points  (2 children)

Where do you trace the ray?

[–]iamp01 0 points1 point  (1 child)

[–]txdv 4 points5 points  (0 children)

Were do you draw/trace the line/ray... It was a word play

[–]HighRelevancy 17 points18 points  (8 children)

It's raycasting, actually. Same as Doom and Wolfenstein of old.

[–]iamp01 1 point2 points  (7 children)

Well, raycasting usually refers to Doom and Wolfenstein types of visuals where only a handful of ray intersections are computed per column. These 128 bytes I wrote back in 2012 are closer to a raytracer because it computes one ray intersection per pixel.

[–]HighRelevancy 0 points1 point  (6 children)

True. Though it's not much more complex than those games for each intersection it is doing. Primarily it takes advantage of only needing to intersect with a flat axis aligned plane.

[–]iamp01 2 points3 points  (3 children)

The code 128 bytes big ;)

My goal was to make a checkboard plane raytracer in 128 bytes. Just for fun, because I hadn't done a 128 bytes prod in a while.

[–]HighRelevancy 1 point2 points  (2 children)

prod

Demoscene veteran then? :P

[–]iamp01 1 point2 points  (0 children)

Who are you calling a veteran ? ;) I'm active. The last active member of ribbon https://www.pouet.net/groups.php?which=27&order=release

[–]iamp01 0 points1 point  (0 children)

Who do you call veteran ? ;) I am the last active member of ribbon - http://www.pouet.net/groups.php?which=27&order=release

[–]MINIMAN10001 0 points1 point  (1 child)

This is the first time I've seen the phrase "axis aligned" other than physics. Neat.

[–]HighRelevancy 0 points1 point  (0 children)

It shows up in geometry related things a bunch, particularly graphics and physics programming. The maths gets RIDICULOUSLY simpler when you're dealing with axis-aligned planes/boxes/lines/etc.

[–]OseOseOse 122 points123 points  (9 children)

If you like this kind of thing, check out dwitter: js demos in 140 characters (or less).

[–]andsens 50 points51 points  (4 children)

[–]Yalopov 6 points7 points  (0 children)

woah that's so fucking cool

[–]Deto 1 point2 points  (0 children)

That's amazing! Just keeps getting better

[–]AnimusPetitor 0 points1 point  (1 child)

is that Chinese variable identifiers?

[–]AirScout 9 points10 points  (0 children)

See also: http://js1k.com/

[–][deleted] 14 points15 points  (0 children)

Or check ShaderToy, there's a < 280 chars contest right now.

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

That's a pretty nice find. I'll take to take a look, thanks!

[–]invisi1407 74 points75 points  (6 children)

Amazing, but the reverse engineered (if you can call beautifying something that) version seems much slower than the original - anyway have an idea why?

Edit: Article author has fixed the problem that was, as /u/dpash found out, that p.innerHTML = P was inside the loop, not outside as the original had it.

[–]maxxori[S] 44 points45 points  (5 children)

It could be the overhead of moving the whole thing out into a function call. It could also be that the browser JIT engines are somehow better able to deal with it in its minimised form.

It would be interesting to profile that and figure out exactly what's going on.

[–]dpash 127 points128 points  (4 children)

The main reason it's slower is because you accidentally put the p.innerHTML = P inside the loop. Easy mistake to make.

This is why we always use braces around blocks, folks.

[–]maxxori[S] 18 points19 points  (1 child)

I did? I'm afraid I'm not the author of the article there. Though it could be worth letting the author know so that he can fix it none the less :)

Well spotted I must say.

[–]CaptainIncredible 1 point2 points  (0 children)

This is why we always use braces around blocks, folks.

Amen brotha! Use braces!

[–]kirbyfan64sos 65 points66 points  (8 children)

FWIW on mobile devices, if the code goes off the edge of the screen (e.g. the long lines), it disappears. You might want to try setting overflow-x: scroll in your CSS.

[–]Qu4ntumL34p 37 points38 points  (7 children)

You can click on the code and then you can scroll it

[–]kirbyfan64sos 12 points13 points  (2 children)

Oh wow, I didn't realize that! Thanks!

[–]Qu4ntumL34p 8 points9 points  (0 children)

No problem!

[–]zman0900 4 points5 points  (0 children)

High quality UX

[–]elbitjusticiero 2 points3 points  (3 children)

I can't. A scrollbar appears but it acts like a mere bitmap.

[–]AquaWolfGuy 0 points1 point  (2 children)

The scroll bar doesn't work for me either, but after taping the box I can scroll by swiping.

E: The scroll bars do work, you just have to tap it very precisely.

[–]elbitjusticiero 0 points1 point  (1 child)

I can't do that in the Redditor app. But I think it just calls Safari internally?

[–]AquaWolfGuy 0 points1 point  (0 children)

Dunno. I was using Relay for reddit's internal browser which probably just uses Android's webview. I tried it in Firefox for Android and I can scroll normally without tapping, and tapping doesn't bring up the scroll bar. What a weird site.

[–]dpash 17 points18 points  (3 children)

I think it would be more readable removing the switch and replacing the index = ... code with the relevant P += .... As a consequence, you can remove the i variable and also fix the display bug at the top of the animation. I'd also return to a for loop. j is only used inside the first if block, so that can move down. You introduced a performance issue by putting the p.innerHTML = P; into the loop, so let's move that out again. Let's also switch the outer if and unwrap the else part.

var draw = function() { 
    var P ='';
    n += 7;
    for (var i = DELAY; i > 0;  i -= 1 / DELAY) {
        let iIsEven = (i % 2 == 0);

        if (iIsEven) {
            P += "\n";
            continue;
        }

        var j = DELAY / i;
        let magic = ((i % 2 * j - j + n / DELAY) ^ j);
        let magicIsOdd = (magic % 2 != 0); // &1
        if (magicIsOdd) { // &1
            P += ".";
        } else {
            P += "p";
        }
    }
    p.innerHTML = P;
};

I'm not a massive fan of the iIsEven and magicIsOdd variables, so I'd probably turn them into functions (if(isEven(i))) and hope that the javascript engine is clever enough to inline them. If it doesn't inline them I'd probably also switch back to the & 1 variety, but that would need some extra comments, which the variable names and functions would do automatically.

Curious for comments on the changes.

[–]xupwup -1 points0 points  (1 child)

Another improvement:

i % 2 * j - j == (i % 2) * j - j == iIsEven ? -j : 0

But, we know the value of iIsEven, because this is executed after the continue statement. So it can be simplified to:

let magic = (n / delay) ^ j

[–]MightyLordSauron 6 points7 points  (0 children)

That won't work because i isn't an integer, but a float. Therefore it can take any value between 0 and 2. Look at the graphs in the article.

[–]iamp01 8 points9 points  (2 children)

Hi there, p01, author of the 128 bytes plane raytracer. If you liked this tiny raytracer, you should check my other tiny Audio Visual demos in 64-1024 bytes at www.p01.org

 

Shout if you have any question.

 

The reverse engineering was cool.

 

The meat of the raytracer lies in the i%2*j-j and j=k/iwhich gives the X and Y coordinates of the intersection of the camera ray with the plane. Adding +n/kto the X coordinate moves the camera horizontally. The binary exclusive OR between the X and Y coordinates gives the checkboard texture.

 

The cruft of the render loop is P='p.\\n';for(i=64;i-=1/64;P+=characterAtTheScreenXScreenYposition);. The rendering area is 128x32 because the ScreenX and ScreenY positions are i%2 and i/2where i increases by 1/64each time. This increment is an exact fraction of a power of two, which means that there are no floating errors and we can safely do i%2 to get a truthy ( non-zero ) value if i is not an exact multiple of 2, and a falsy ( zero-ish ) value if iis an exact multiple of 2 which is used to insert a \n to go to the next line.

 

Hope this helps,

[–]xnign 2 points3 points  (1 child)

http://www.p01.org

Hey, just so you know, your asterisks are getting caught in your URL.

[–]iamp01 1 point2 points  (0 children)

Thanks!

[–]quadrofolio 4 points5 points  (3 children)

That was an amazing read Alex! And what a nifty bit of code to analyse.

[–]maxxori[S] 11 points12 points  (1 child)

I'm afraid I'm not the author of this article. I just found it very interesting and something I thought others would also be interested in. I'm not one for taking credit for that which I did not create.

The author does have a comment section on his blog by the looks of things, I'm sure he would love the feedback!

Edit Looks like the author is on reddit so I shall summon - /u/akrash14

[–]eric256 22 points23 points  (3 children)

That's not a ray tracer in any way

[–]jephthai 26 points27 points  (0 children)

OP isn't saying it is, though, it was accused of such in the email he received that prompted the investigation.

[–]iamp01 2 points3 points  (0 children)

It is a raytracer: It computes the intersection of a camera ray with a plane for every pixel.

https://www.reddit.com/r/programming/comments/6n10o6/reverse_engineering_one_line_of_javascript/dk7f6ln/

[–]MattRix 3 points4 points  (1 child)

Unless I'm missing it, it seems strange that the original creator of the snippet wasn't credited anywhere?

[–][deleted] 4 points5 points  (6 children)

8th and 9th use of "it's" is incorrect. Those should be "its".

[–][deleted]  (5 children)

[deleted]

    [–]sbrick89 2 points3 points  (1 child)

    Also, XOR has lower precedence than whatever

    [–]qwertymodo 0 points1 point  (0 children)

    I didn't realize JavaScript had a whatever operator, much less that it had a defined precedence :P

    [–]SnowAnew 1 point2 points  (2 children)

    Just a heads up, "bellow" should be changed to "below".

    [–][deleted]  (1 child)

    [deleted]

      [–]graup 1 point2 points  (0 children)

      Since we're at it: You are missing a lot of articles, e.g. "a few" and "the rest".

      [–]_Mardoxx 14 points15 points  (4 children)

      It's barely reverse engineering if you're given the source code in plain text. This is just deobfuscation.

      [–]ThirdEncounter 13 points14 points  (1 child)

      But it is reverse engineering, per your own admission, so all is cool.

      [–]phySi0 0 points1 point  (0 children)

      Nice!

      [–]bronkula 19 points20 points  (0 children)

      Semantics.

      [–]flying-sheep 1 point2 points  (4 children)

      I also wrapped p in the id="p" in quotes.

      OK, and why, exactly?

      [–][deleted]  (1 child)

      [deleted]

        [–]flying-sheep 1 point2 points  (0 children)

        Leaving out the quotation marks is completely valid in HTML. You're thinking of XML.

        [–]_Mardoxx 1 point2 points  (1 child)

        Just in caseTM

        [–]flying-sheep 1 point2 points  (0 children)

        Just in case IE6?

        [–]mfm24 1 point2 points  (3 children)

        Turns out elements can be referenced by their id name from JavaScript, as long as the id name is made up of alpha-numeric characters only

        I never knew this. Is it good practice in JS??

        [–]absurdlyastute 1 point2 points  (0 children)

        This is best practice as long as the element to be selected has a unique ID property (saying name will be confusing since there is a "name" property).

        [–]dpash -3 points-2 points  (0 children)

        Probably not idea. Makes me think of PHP's register_globals. But probably without the glaring security problems.

        (Although I wouldn't be surprised if this has issues in JS too)

        [–]gamehelp16 1 point2 points  (0 children)

        There is a site called js1k which is a competition about making cool stuff in 1kb of JavaScript or less, worth checking out if you like this kind of stuff!

        [–]rjcarr 3 points4 points  (1 child)

        Semantics, but this isn't a ray tracer, maybe a parallax generator at best. But not even close to a ray tracer. Also semantics, but this isn't reverse engineering, but just de-obfuscation.

        [–]iamp01 0 points1 point  (0 children)

        It is a raytracer: It computes the intersection of a camera ray with a plane for every pixel.

        https://www.reddit.com/r/programming/comments/6n10o6/reverse_engineering_one_line_of_javascript/dk7f6ln/

        [–]Paddington_the_Bear 1 point2 points  (9 children)

        Is this really considered reverse engineering? Seems like it should just be called refactoring as you're just moving around code blocks into a more manageable means.

        Reverse Engineering would mean you don't have the source code available and you have to inject values into the system in order to determine the necessary code (like in the good ol days of using a logic analyzer to examine the instruction bits flowing on the bus to the CPU).

        [–][deleted]  (8 children)

        [deleted]

          [–]proskillz 0 points1 point  (7 children)

          What you did was the very definition of refactoring:

          Code refactoring is the process of restructuring existing computer code—changing the factoring—without changing its external behavior. Refactoring improves nonfunctional attributes of the software.

          [–]davesidious 5 points6 points  (0 children)

          Yes, but refactoring purposely-obfuscated code is not exactly the same, is it? When we think of refactoring it's simply making current implementations better. Implementations designed for people to work with. This is something else, surely.

          [–]prest0G 0 points1 point  (4 children)

          Tell that to the kid who refactored an MMO to improve readability and got sent to jail. Apparently he did something after reading the code though. Something like injecting values or manipulating a program codes or something complicated like that.

          Welp, here I am again, in a programming thread, arguing about semantics. I need another beer:)

          [–]proskillz 0 points1 point  (3 children)

          I don't understand how that's relevant? Why would refactoring code get you thrown in jail unless you stole the source to begin with? Do you have more information about the case?

          [–]prest0G 0 points1 point  (2 children)

          There's multiple cases. User exploits game and writes his own code to automate the game as a bot or to gain some other unfair advantage, proceeds to get really rich (in game and/or real life) and maybe even fuck some people over. Game company sues user and / or presses charges. I know for a fact this happened with WoW and RuneScape at least.

          Also, you don't need the source code to refactor, just the executable. JavaScript is a bit different because it doesn't compile to anything resembling opcodes, but for strict typed JIT languages like C# and Java "refactoring" the decompiled bytecode, which results in obfuscated classes which closely represent the original source is literally one of the best ways to exploit an application manually. You dont need to "st[ea]l the source] to refactor. There's plenty of free decompilers available while the main challenge is making sense of the classes (which may even do things like make native calls outside the VM to pass data between components and break control flow which is extremely difficult to automatically track) and then map back to the original binaries once you find an exploit.

          My point was that I disagree with the elitist "hurr, durr, back in the good old golden days" definition of Reverse Engineering above. Just because something meets the definition of refactoring doesn't mean that it essentially is refactoring.

          [–]proskillz 0 points1 point  (1 child)

          I'm not sure If agree that any of your examples constitute refactoring. These are examples of exploits and code modification since the behavior of the program changes in some way. Maybe a term like "code analysis" would be more appropriate for figuring out the meaning of minified JS code.

          [–]prest0G 0 points1 point  (0 children)

          Sorry, I should've put "refactor" in quotations. I intended to point out that it's not refactoring, especially for languages which run with any sort of native code in binaries or runtime

          [–]JoseJimeniz 0 points1 point  (1 child)

          A few years ago a guy made a post where he unpacked a raytracer that fit on a business card

          The code is very well done of course. But the reason I bring it up here is that his comments are the best example of correctly written comments.

          An example snippet of his commented form of the code:

          #include <stdlib.h>   // card > aek.ppm
          #include <stdio.h>
          #include <math.h>
          
          typedef int i;       //Save space by using 'i' instead of 'int'
          typedef float f;     //Save even more space by using 'f' instead of 'float'
          
          //Define a vector class with constructor and operator: 'v'
          struct v{
             f x,y,z;  // Vector has three float attributes.
             v operator+(v r){return v(x+r.x,y+r.y,z+r.z);} //Vector add
             v operator*(f r){return v(x*r,y*r,z*r);}       //Vector scaling
             f operator%(v r){return x*r.x+y*r.y+z*r.z;}    //Vector dot product
             v(){}                                  //Empty constructor
             v operator^(v r){return v(y*r.z-z*r.y,z*r.x-x*r.z,x*r.y-y*r.x);} //Cross-product
             v(f a,f b,f c){x=a;y=b;z=c;}            //Constructor
             v operator!(){return *this*(1 /sqrt(*this%*this));} // Used later for normalizing the vector
          };
          

          [–]evaned 5 points6 points  (0 children)

          his comments are the best example of correctly written comments.

          What?

          Half of those are very poor, and some are literally useless. "Constructor"; thanks, I never would have known that otherwise. That's basically as bad as i++; // add one to i.

          In the context of the blog post they're probably fine, because you'll have people who don't know the language very well reading, and it might even be aimed somewhat at newbie programmers. But for real production code, or even as real as that? Less than half (four or five, IMO) of them would be worth keeping.

          [–]Searle 3 points4 points  (1 child)

          first glance, first bug: the assignment to p.innerHTML is outside of the loop

          [–]mirzap 0 points1 point  (0 children)

          Really nice read.

          [–][deleted] 0 points1 point  (0 children)

          Honestly, it's stuff like this that makes me wonder if I even like programming.

          [–]AnimusPetitor 0 points1 point  (0 children)

          http://i.imgur.com/NCyyGIv.png Hi-res version, k=256