Posted in 2017, aframe, JavaScript, LearningItMyWay, Lightbeam, Outreachy

Lightbeam goes virtual!

Here is a gif from my latest aframe experiments for Lightbeam.

xk9wcnyeom

For¬†Mozfest 2017, I had submitted the following proposal – ‘Lightbeam, an immersive experience‘. While the proposal is still being reviewed, I have been experimenting with Aframe and the above gif is an initial proof of concept ūüôā

Here is the excerpt from the proposal:

What will happen in your session?

Lightbeam is a key tool for Mozilla to educate the public about privacy. Using interactive visualisations, Lightbeam’s main goal is to show web tracking, aka, show the first and third party sites you interact with on the Web.

In this session, the participants will get to interact with the trackers in the VR world thus creating an immersive Lightbeam experience. With animated transitions and well-crafted interfaces, this unique Lightbeam experience can make exploring trackers feel more like playing a game. This can be a great medium for engaging an audience who might not otherwise care about web privacy & security.

What is the goal or outcome of your session?

The ultimate goal of this session is for the audience to know and understand about web tracking.

While web tracking isn’t 100% evil (cookies can help your favourite websites stay in business), its workings remain poorly understood. Your personal information is valuable and it’s your right to know what data is being collected about you. The trick is in taking this data and shacking up with third parties to help them come up with new ways to convince you to spend money and give up more information. It would be fine if you decided to give up this information for a tangible benefit, but no one is including you in the decision.

Advertisements
Posted in 2017, JavaScript, LearningItMyWay, Lightbeam, Outreachy

Lightbeam – All the Math!

In this post I shall discuss all (most of) the math involved in the visualisations. A quick recall:

Lightbeam is now responsive and fully interactive.

coLIwC8qTB
Drag, zoom and responsiveness!

Tooltips

The math behind tooltips is the sequel to my blog post¬†Lightbeam ‚Äď Tooltips (SVG).

Read this blog post to understand Lightbeam’s migration from SVG to Canvas.

Screen Shot 2017-08-13 at 14.53.15

Ignore the transforms and the inversions (this.transform.invert) in this post. Those are part of d3-zoom and explaining the math of this and d3-force is beyond the scope of this blog post.

mousemove event is registered on the canvas element itself.

Screen Shot 2017-08-13 at 15.47.56

The mouse <clientX, clientY> positions are re-calculated w.r.t the canvas’s bounding rectangles. This ensures the mouse coordinates are confined to the canvas’s area.

getNodeAtCoordinates(x, y) returns a node, if a node is present at the given <x, y> values.

D3’s force layout has simulation.find(x, y[, radius]¬†which returns the node closest to the position <x, y> with the given search¬†radius. I chose to write isPointInsideCircle()¬†to find out if a node exists at the given¬†<x, y>¬†values. The intention here is to isolate the logic from D3 specific as much as possible.

isPointInsideCircle():

When you hover over the canvas, and if the mouse coordinates are inside any circle, then there is a node present at these coordinates.

Screen Shot 2017-08-13 at 16.30.39

The point <x, y> is

  1. inside the circle if d < r
  2. on the circle if d = r
  3. outside the circle if d > r

Square roots are expensive. Hence d is compared with r*r!

CSS:

Tooltip position:

Screen Shot 2017-08-13 at 16.43.45

The tooltip has position: absolute.

Because of this property, there is a need to check the tooltips’ left¬†property doesn’t exceed the canvas’s right¬†property, else there will be horizontal scrollbar on the parent container because of overflow-x.

x+tooltipWidth >= canvasRight takes care of the overflow and sets left to x-tooltipWidth.

Setting left to x-tooltipWidth/2 ensures the tooltip arrow is centre aligned to the node.

Favicons

If a favicon exists for a given node, then it is drawn.

Screen Shot 2017-08-13 at 16.57.19

The favicon is drawn at the centre of the circle (firstParty) or triangle (thirdParty).

Screen Shot 2017-08-13 at 17.00.02

A square that fits exactly in a circle has a side length of sqrt(2) * radius.

firstParty & thirdParty nodes

Given that we are drawing on a canvas, firstParty is a circle on the canvas. thirdParty is an equilateral triangle.

Screen Shot 2017-08-13 at 20.18.35

Given the centre of the circle is at <x, y>, r is the radius of the circumcircle and dr is the radius of the incircle.

equilateral triangle
equilateral triangle

zoom and drag

d3-zoom and d3-drag are used to achieve the zoom and drag behaviours respectively. It is quite complex when the two are combined. If you click and drag on the background, the view pans; if you click and drag on a circle, it moves.

d3-drag requires a dragSubject. I am using the same getNodeAtCoordinates(x, y) function which is used to show the tooltips and the logic remains same. This is how drag and zoom are combined for Lightbeam. If there is a node, (dragSubject) then it drags, else it pans.

Here is the d3-zoom implementation.

Screen Shot 2017-08-13 at 20.43.30

The tricky part here is the need to distinguish between two coordinate spaces: the world coordinates used to position the nodes and links, and the pointer coordinates representing the mouse or touches. The drag behaviour doesn’t know the view is being transformed by the zoom behaviour, so we must convert between the two coordinate spaces.

This is where transform.invert or transform.apply come into play.

I hope I have done justice to the math in this post!

Posted in 2017, LearningItMyWay, Lightbeam, Outreachy, Random

Lightbeam updates

Here are two quick updates about Lightbeam because I can’t contain the excitement to myself.

Lightbeam goes responsive, yayyyyy!!!

lb.gif
Responsive Lightbeam

Responsive UI

I am extremely happy for achieving this today. Making the UI responsive was there in our to-dos, but this one got done today accidentally in an attempt to answer few of the comments on one of my PR. CSS grid is used and I must say the fr unit is so handy.

Mozfest proposal submission

Proposed a session for the Mozilla Festival 2017, London. Here is the proposal. The coolest part here is that the google-form submission automatically gets created as a GH issue.

Posted in 2017, JavaScript, LearningItMyWay, Lightbeam, Outreachy

Lightbeam – SVG to Canvas

SVG is the preferred choice for D3. But when you expect a lot of DOM nodes (yes, in the case of Lightbeam) you need to start worrying about the DOM performance and have to take the call to step out of the SVG comfort zone. We chose Canvas over SVG for Lightbeam 2.0!

In this post, I would like to highlight the key points of drawing and interactivity on the HTML5 Canvas element.

d3andcanvas
Link

 

In our case, we followed the first approach – using D3 solely for its functional purpose (D3’s force layout algorithm) and then drawing onto the canvas.

D3 joins

Normally, when you follow this approach, you are trading off D3’s super rich data binding and joining functionality. It means you‚Äôre only drawing your graph once – you‚Äôre not expecting new data that would require a graph redraw or update. This is why D3 with some dummy HTML nodes is a preferred way in order to retain the data binds and joins. D3’s joins are a way to dynamically update the graph without having to redraw the whole thing over and over again.

Screen Shot 2017-07-28 at 11.51.35
Typical example of enter, update and exit methods in D3 joins. Link

The dummy HTML element approach

Here is an example of using custom aka dummy HTML elements to render D3 on canvas.

Screen Shot 2017-07-28 at 11.59.52
custom elements

custom is definitely not a standard DOM element type, so it will not be rendered in any way and will live only in memory (virtual DOM). It is used as a container for other dummy nodes.

How does Lightbeam update dynamically without D3 joins?

Even though we followed the first approach, Lightbeam has dynamic updates. Luckily, D3’s force layout algorithm takes care of the new node¬†and link¬†updates/additions and we managed to use D3 only for its functionality without the dummy element approach. Here is the PR.

Interactivity on the canvas

Canvas is a single DOM element and mouse interactions on the canvas can be sometimes tricky because you don’t have independent access to the nodes and links (or any graph element). In our case, we have the following interactions:

  • On hover over the nodes (websites) show tooltips with the name of the website
  • Drag the graph
  • Zoom in and zoom out the graph
  • Panning – shifting the view of the graph

At the time of writing this blog post, only tooltip based interactivity is achieved. PR

I shall explain tooltip based canvas interactivity in the next post.

Resources:

Initial performance results

I have never done performance testing using browser devtools. When we migrated from SVG to Canvas I was curious to know the performance results of using canvas. For a given small sample of websites, here are the test results. Canvas has a rendering score of 4.3 milliseconds ūüôā

old
Old Lightbeam (SVG)
new-svg
New Lightbeam (SVG)
new-canvas
New Lightbeam (Canvas)
Posted in 2017, JavaScript, LearningItMyWay, Lightbeam, Outreachy

Lightbeam – Tooltips (SVG)

In this blog post I want to share my lessons learned from creating tooltips for the Lightbeam visualisations.

tooltip-gif
Tooltips – canvas based solution

Visualisations are the major part of this Outreachy РLightbeam internship. We use D3-force to create a simulation for an array of nodes (websites), compose the desired forces, and then listen for tick events to render the nodes as they update. When we started, the preferred graphics renderer was SVG. We now have a Canvas based implementation because this is more efficient than SVG. Here is an article why canvas is better than svg and here are the initial results.

Having said that, lets talk about tooltips.

TL;DR

SVG based solution

I had first implemented tooltips(text labels) on the SVG version. Here is the PR.

tooltips-svg
Text labels – svg based solution

In SVG, you can render every node as a DOM element. This is why SVG isn’t the right solution for graphs with too many nodes, because it can easily slow down the browser’s page loading time due to too many nodes.

Since this is an SVG based solution, I chose text-labels over tooltips.

Tooltips vs text-labels

Tooltips come with an additional overhead of dynamically updating the x,y coordinates and the title. Text labels are rendered like ordinary DOM elements, the SVG <text> element, one for each node(website).

But using tooltips, you can cut down the number of text nodes. As opposed to having one text node for each node(website), there is one tooltip (DOM element) whose position and content is dynamically updated.

I shall discuss the tooltip based solution in the canvas implementation.

Implementation

1) Text labels in SVG

  • By default, the visibility of text-labels is set to hidden.
  • All text-labels have a common class textLabel.
  • On hover, over the circular nodes, the corresponding text-label’s visibility is changed from hidden¬†to visible.
  • For each node(website) there is a corresponding <text>¬†element.
  • In order to identify and set the visibility of the right <text> corresponding to the hover on node(website), every <text>¬†element is assigned an additional class text-i¬†where i¬†is the index corresponding to each node(website).
  • When node(website) with index i¬†is hovered, the corresponding <text>¬†with class text-i is set to visible.

drawlabel

2) Hover on the nodes(websites)

In order to achieve the hover effect, mouseenter and mouseleave event handlers are registered on each node(website).

drawnodes

Fixing Bugs

hover-flicker

After the initial implementation, there was a lot of flicker when the text labels were made visible on node hover. Although it isn’t very clear from the above gif, notice the time taken to display http://www.google.com on the third node. Looks like gifs aren’t good at capturing flickers (I need to find out a better solution), but the time delay to show the third label in the above gif === flicker effect.

1) mouseenter vs mouseover

At first, I thought the flickering was due to the mouse events. Later, I found out that these mouse events had nothing to do here. However, it is worth making a note of the above two mouse events. Link

mouseenter Event reference MDN

Below is the DOM for the SVG version:

Screen Shot 2017-07-21 at 16.55.12

Since the nodes/circles are independent of the text labels, and have no direct descendant nodes, the mouse events had nothing to do with the flicker.

2) SVG and z-index

After wracking my brains for a while, I figured out the cause for the flickering of labels. If you carefully examine the above gif, you will notice that the lines are on top of the circles. On mouse hover over the nodes, it was mouseenter¬†for circles and labels were made visible. But, when the mouse accidentally appeared on top of the lines (because you don’t realise you are doing so) it caused the mouseleave¬†for circles and the visibility was getting toggled.

When I figured out this cause, the obvious solution to run in my head was to set z-index for circles to be greater than the lines. Despite this, the flickering was still there. On further investigation, this is what I found from the SVG specification:

Screen Shot 2017-07-21 at 17.16.24

z-index in SVG is defined by the order the elements appear in the document. You will have to change the element order if you want to bring a specific shape to the top. Drawing lines first, followed by drawing circles fixed the flickering bug.

It was good to learn about z-index in SVGs.

To be continued… tooltips – a canvas base implementation

Posted in 2017, LearningItMyWay, Random

princiya.com

Yesterday, I launched my website! Yayyy… after months of purchasing the domain name, here it is..¬†princiya.com‚ô•

It is a Hugo powered website and hosted on Github. I started with Jekyll, but ended up using Hugo. Thanks to my Groovy on Grails knowledge, it helped me with setting up and understanding Hugo.

For those of you wondering what’s Jekyll or Hugo,¬†here¬†is an interesting article. In short, both Jekyll & Hugo are static website generators.

To start with, I am using the¬†Kube¬†theme for my website. I would continue to use this space (wordpress) for blogging. For now, I intend to use¬†princiya.com¬†to document my¬†‘Today I Learned’¬†series of articles.

Following are the (must) to-dos for my website:

  • SEO
  • Pagination for blog posts with next and previous links
  • Sort¬†blog posts based on date
  • List all categories, tags etc for the posts
  • Logo, favicon
  • Google Analytics
  • Page to showcase my talks/presentations

I guess, I would figure out remaining things as I go. ‘Better late than never’, I now have my personal website ūüôā

Happy Monday!

Posted in LearningItMyWay

CORS

CORS – Cross Origin Resource Sharing

This is a sequel to my post on cURL.

“Cross-origin resource sharing (CORS) is a mechanism that allows restricted resources (e.g. fonts, JavaScript, etc.) on a web page to be requested from another domain outside the domain from which the resource originated” from wiki.

The above explanation is self explanatory. For instance, a resource loaded from Domain A (http://A:3000) such as an HTML web page, makes a request for a resource on Domain B (http://B:4000). The Same-Origin Policy restricts the browser from performing certain actions by scripts or documents based on the origin. The origin is everything in the URL before the path (e.g., in http://A:3000/images/bg.png, http://A:3000 is the origin and /images/bg.png is the path). For certain actions, the browser will compare origins and, if they don’t match, won’t allow things to proceed.

We kept getting CORS error, when we were trying to test our REST API. The server sided REST API logic was written on Node.js and was running on port 3000 on one system. The client side logic was also written on Node.js and was also running on port 3000, but on another system. I had initially tested the API using POSTMAN, and it worked great. But, when we tried integrating the front-end with the back-end, it was erroneous. And it was CORS. I had encountered something similar related to CORS, way back in 2011, during my first job, but it was resolved in few minutes. I cannot recall the exact issue, all I remember is that it had to do only with JQuery (front-end only).

This time, I tried understanding CORS in more detail.

Let’s assume, the server-side code was running on http://A:3000 and the client-side code was running on http://B:3000. We were trying to test the login api, i.e. front-end accepts username/password through a form and on button click, submits the values (HTTP POST) to the server. The server then validates the username/password combination. If successful, server returns the user id, else, returns appropriate error messages. We had agreed upon both the input and output to be JSON only.

Things looked alright, when the input type was x-www-form-urlencoded, i.e. normal form data being passed through JQuery AJAX request. But things went awry, when we restricted it to JSON only (POSTMAN still had no issues).

Following is the list of errors when we tried with JSON input, and how I went about solving each one of them.

1. http://B:3000 made a POST request to http://A:3000/login. But instead of POST, it always made a OPTIONS request.

Now the first thing to wonder about was why did this happen. To be honest, I wasn’t sure about the HTTP OPTIONS method until then. But this is what it is. “This method allows the client to determine the options and/or requirements associated with a resource, or the capabilities of a server, without implying a resource action or initiating a resource retrieval.”
Nobody uses this OPTIONS method, but it is used for CORS access to API’s. When doing a CORS request, the browser will do a ‘preflight’ OPTIONS request to discover if the server will answer the real request.

The next thing to wonder was what is this preflight.

CORS requests are usually preflighted. This request is basically there to ask the server if the full request is permissible. If POST is used to send request data with a Content-Type other than application/x-www-form-urlencoded, multipart/form-data, or text/plain, e.g. if the POST request sends an XML/JSON payload to the server using application/xml or text/xml or application/json, then the request is preflighted. Only if the server responds positive to this preflight request, the actual POST request will be sent to the server.

At this point it was clear why the POST request always came as an OPTIONS request for JSON input and not for x-www-form-urlencoded.

2. After I understood about OPTIONS method and the preflight request, following was the next error. XMLHttpRequest cannot load http://A:3000/login. The ‘Access-Control-Allow-Origin’ header has a value that is not equal to the supplied origin. Origin ‘null’ is therefore not allowed access.

I was running test.html locally on the browser as file:///E:test.html. Running it this way, always sets the Origin to Null in the request header. This took quite some time for me to understand and resolve. I then created another local Node.js server running on port 4000 to serve test.html. Alternative to this could have been serving test.html through WAMP Server/Apache on port 80. This resolved the null origin issue. The origin was now set to http://localhost:4000.

3. Following was the third error. XMLHttpRequest cannot load http://A:3000/login. The ‘Access-Control-Allow-Origin’ header has a value http://B:3000 that is not equal to the supplied origin. Origin http://B:3000 is therefore not allowed access.

This wasn’t scary, like the earlier one. I could make sense, what was happening here. On my server sided logic in Node.js, I had restricted “Access-Control-Allow-Origin” to http://B:3000 only. res.header("Access-Control-Allow-Origin", "http://B:3000");
On the client side code, I had to add the below line to JQuery AJAX method. When the CORS makes a preflight request, both the client and server should have the same headers set.
beforeSend: function (request) { request.setRequestHeader("Access-Control-Allow-Origin", "http://B:3000"); }

This is the full JavaScript working code after understanding about CORS, OPTIONS, preflight and setting headers through AJAX.

var login = {username: "sadmin", password: "sadminasd"}; $.ajax({ dataType: "json", beforeSend: function (request) { request.setRequestHeader("Access-Control-Allow-Origin", "http://B:3000"); }, contentType: "application/json", type: "POST", data: JSON.stringify(login), url: 'http://A:3000/login', success: function(data) { console.log("Success", data); //alert("Success "+data); }, error: function(err) { console.log("Error", err); //alert("Error "+err) } });

At one point, we took the approach of solving this problem using JSONP. JSONP was initially the solution prior to CORS for making cross domain requests. But JSONP supports only GET and not POST.

In my next blog, I shall talk more about JSONP and also about JSONP vs CORS.