Zero budget eye tracking. Clickmaps.

28 Jul

Eye tracking is quite expensive. The hardware and software have astounding capabilities, but sometimes it’s hard to justify the expenditure. But there is a technique that can imitate it until certain point. You can find where your users are clicking using just a bit of javascript and server side programming. Find out how…
NOTE: This post has been improved at The definitive heatmap

Ojo de usuario

We need to follow three simple steps:

  1. Create a function that follows the mouse movement and calls an external script in the server for every click
  2. Call the server using an asynchronous method like HttpRequest
  3. Use a logging program to write a log in the server

Capturing the mouse position

The first thing to do is to find out the mouse position. The best way to approach this is to link a javascript file from the end of your html document. The file should be something like this:

/*detect browser*/
var IE = document.all?true:false
if (!IE) document.captureEvents(Event.MOUSEMOVE)
var tempX = 0
var tempY = 0
/*call the function*/
document.onclick = getMouseXY;
function getMouseXY(e) {
if (IE) {
tempX = event.clientX + document.body.scrollLeft
tempY = event.clientY + document.body.scrollTop
} else {
tempX = e.pageX
tempY = e.pageY
var url="/perl/"+tempX+"&y="+tempY;
return true
if( document.getElementsByTagName ){
var links = document.getElementsByTagName( 'a' );
for( var i=0; i < links.length; i++ ){ links[i].onclick = function(){ return getMouseXY(this); } } }

This script filters the browser, inits the click controller and calls an external function guardar that will use the HttpRequest object to call the server.


The HttpRequest object allows us to call the server outside the normal navigation flow. What we want now is to store the mouse position every time the user clicks in the page. We are going to use the following javascript code:

var xmlDoc = null ;
if (typeof window.ActiveXObject != 'undefined' ) {
xmlDoc = new ActiveXObject("Microsoft.XMLHTTP");
}else {
xmlDoc = new XMLHttpRequest();
function guardar(url){ "GET", url, true );
xmlDoc.send( null );

Just like before, we have to use a different approach for internet explorer and all other browsers, since explorer uses its own implementation, to create an asynchronous call object (xmlDoc). Then, we use this object to call the server in the background using a GET method to send the mouse position.

The server side

From the server side, there's no need to send anything to the user. We only need to store the variables sent by the query string in a file (or database), mainly the coordinates, the referer and the IP address. The following perl/CGI code should work as a proof of concept

use CGI;
use strict;
my $q=new CGI; #create a new GCI instance
print $q->header(); #print headers
open (LOG,">>log.txt") ||die; #open log file to append
print LOG $q->Vars; #and prints all the data
print LOG "\t";
print LOG "\t";
print LOG "\n";
close (LOG);

This script willl write a line for every click in our page.

To do

Now it's time to analyze the data we got. We would need to filter the log and make a clickmap. The English version of the second part is still unavailable, but you can read the Spanish version if you want. I'll try to translate it as soon as possible

There are many other things that can be done with this approach. You can, for example, write a log with every mouse movement, to keep track of the users' navigation. If you find some creative uses for this, please comment.

Would like to share? These icons link to social bookmarking sites where readers can share and discover new web pages.
  • Digg
  • Reddit
  • Facebook
  • Google Bookmarks
  • Ma.gnolia
  • TwitThis
  • LinkedIn

12 Responses to Zero budget eye tracking. Clickmaps.



July 31st, 2006 at 23:23

very interesting, but if i understand it well, it does not show the right result e.g. for your web, which is centered. You also probably wrong calculate the click position in the document, because in some browsers it returns position in document, in some of them position in the window. See this link:
For centered pages beware also that width change if there is a vertical scroll or not.

You should also monitor the date/time, as the positions of elements may change during the time (e.g. you add an article with different lenght and the rest are moved)

It would be great to have also an automatic function, which would return it for a particular document and a time period

It would be also great to result in clasical heatmap with different collors, but that would be much harder.



July 31st, 2006 at 23:33

btw this is how fillip do the heatmap with two collors and that is a great idea:
I’m selecting all dots for a specific image. Then I’m loading a blue base image (PNG) that I prepared in PhotoPaint. Then I’m painting small red squares with lots of transparency. Then in a second run I’m painting smaller yellow squares with even more transparency, and then I’m savin the JPG. The base PNG must be high color for good results. I’m recreating the images every 10 minutes after heavy traffic brought the site down when I created every image after a click.
I wonder that there is probably special filter for IE for making the page collorless, so it may be used for a background



August 1st, 2006 at 11:55

Hi noname!
I noticed some of these shortcomings and wrote about them in the second part of the post (

This is just a proof of concept. Of course, we will need to take care of many concerns before running it on the wild, but is a good tool if you can bring your trial users to your premises.



August 2nd, 2006 at 20:02

I wrote a -quite long- post about making heatmaps using colors. You can find it at /english/how-to-make-heat-maps-in-the-cheap



August 10th, 2006 at 10:14

i would suggest using this piece of code to get the mouse position:

var xpos = e.pageX || (event.clientX + (document.documentElement.scrollLeft || document.body.scrollLeft));
var ypos = e.pageY || (event.clientY + (document.documentElement.scrollTop || document.body.scrollTop));
var mpos = array(xpos, ypos)
return mpos;

Here you always get the right result with the logical OR …



August 10th, 2006 at 13:09

Thank you so much, Andrew.

I am working in a new version and I´ll try your code as soon as possible.



August 10th, 2006 at 16:36



August 10th, 2006 at 16:38

Sorry about the braces. Just forget them, i had put some a-tag in there but it was filtered out i guess.



August 10th, 2006 at 16:49

By the way, i haven’t found any ‘document.onclick’ in different manuals.
According to documentation only ‘document.body.onclick’ exists.
Just to mention.

Have fun, Andrew.



August 10th, 2006 at 16:58

But your code works for me in firefox, thank you very much, it quite helped me out!



August 10th, 2006 at 17:26


after ‘ function getMouseXY(e) { ‘

i added this line and it also worked in IE then:

if (!e) e = window.event;

before that i had some problems in IE.



August 10th, 2006 at 17:50

Oh just realized my post of ‘trackiung every click’ is obsolete.
Just didn’t see it does track everything.

Shame today i seem to be blind somehow…
Just ignore it.