85

I am looking for an "eyedropper" tool, that gives me the hex value of the pixel the mouse cursor is under, in JavaScript for a CMS.

For Firefox, there is the excellent ColorZilla extension that does exactly that. However, it's FF only of course, and I would really like to deliver the tool along with the CMS.

A dutch developer has had the very clever idea of using a combination of Ajax and PHP's imagecolorat() to find out the Pixel colour on an image. But that limits the range to images I can access server side, and I'm really dreaming of a universal tool.

I will work with one of these approaches, but would much prefer a cross-browser, Javascript or Flash based way that requires no server-side fiddling and no installing of extensions.

I am also interested in any IE specific solutions that do what ColorZilla can do - I could live with supporting IE and FF only, though a cross browser solution would of course be ideal.

Zach Saucier
  • 24,871
  • 12
  • 85
  • 147
Pekka
  • 442,112
  • 142
  • 972
  • 1,088
  • It's not possible in Javascript; I'm not sure about Flash. – SLaks Dec 20 '09 at 14:46
  • In theory, you might be able to process the style information associated with the topmost element under the mouse cursor, and determine what the colour of the element *should* be, if it *isn't* an image or canvas. In practice this would probably drive you insane, dealing with borders and margin and browser quirks. I think Elijah's answer is the only practical one. – Aaronaught Jan 03 '10 at 20:10

12 Answers12

89

It's not possible with JavaScript as it goes against cross-domain security. It would be very bad if you knew what pixels made up the image, http://some-other-host/yourPassword.png. You can only tell the color of the pixel under the mouse if either the mouse is over a canvas or an image element of the same domain (or an image element of another domain which is served with an Access-Control-Allow-Origin: * header). In the case of the canvas, you would do canvasElement.getContext('2d').getImageData(x, y, 1, 1).data. In the case of the images, you would have to draw them to a canvas with:

var canvas = document.createElement("canvas");
canvas.width = yourImageElement.width;
canvas.height = yourImageElement.height;
canvas.getContext('2d').drawImage(yourImageElement, 0, 0);

And then just use the previous method explained for canvases. If you must be able to convert to various representations of color values, try my color.js library.

Also, you're never going to be able to support IE <9 (that's assuming that IE9 supports canvas) and using Flash won't help as it can't read the pixel data of the document either.

Eli Grey
  • 35,104
  • 14
  • 75
  • 93
  • 6
    Interesting aspect about security, and thanks for the Canvas angle. However, I could live with only being able to access pixels within my domain. – Pekka Dec 20 '09 at 18:24
  • Thanks Elijah. I am somewhat saddened that this is limited to Firefox, but that's not your fault and this is definitely the simplest solution. I will probably combine it with a (slower) server-side fallback option to cater for IE. Accepting your answer. – Pekka Jan 07 '10 at 19:27
  • I see no reason why this would be a security issue. As it is JavaScript can access the raw text of a password from any `input:password`. – zzzzBov Jun 06 '11 at 15:01
  • 1
    What would stop me from embedding `yourPassword.png` into the canvas, then sampling it there? (assuming I know the url) – hughes Nov 08 '13 at 17:04
  • 1
    @zzzzBov, @hughes, you're missing the point: if you could grab any pixels on the screen, then you could look at values in a different browser window or even on a PDF bank statement, in a different window. No one should be running untrusted JS on a page that handles sensitive information, and serving sensitive files should involve proper authentication. You would need to hijack the user's session in order to access their `password.png`. – Dan Ross Jan 11 '14 at 22:04
  • 2
    @danross, js doesn't have cross-sandbox access for executing scripts (unless given explicit permission) there's no reason an api can't be restricted to color values from frames that are within the same security sandbox. – zzzzBov Jan 12 '14 at 03:24
  • @zzzzBov yes, that makes sense. It could get a little complex, but it would be doable. An iframe with a different security context should be a black rectangle to the script, except where content from the current sandbox is positioned over it. – Dan Ross Jan 13 '14 at 01:54
  • @danross, I think it's a better use case for a `null` return value, but that's something that would be better ironed out by one of the whatwg mailing lists – zzzzBov Jan 13 '14 at 04:13
  • So Can we get the pixel color on the corner of youtube video which is embed as `iframe`? throws cors exception. But who knows, maybe there is a hack. demo `http://plnkr.co/edit/3PWdeY8lVMaOFRsLoarq?p=preview` try to call `document.querySelector('iframe').contentWindow.document` from console – Medet Tleukabiluly Nov 22 '14 at 15:52
22

Using a technique called Browser Timing Attack, it is possible to (sort of) determine the color of any pixel, even on iframes.

Basically, this technique measures the time to render an SVG filter on an element, rather than the color itself (requestAnimationFrame() allows to measure time with a much better accuracy than setTimeout()). Depending on the current pixel color, the filter takes more or less time to apply. This makes it possible to determine if a pixel is the same color as a known color - for instance black or white.

More details in this white paper (pdf): https://www.contextis.com/media/downloads/Pixel_Perfect_Timing_Attacks_with_HTML5_Whitepaper.pdf

By the way: yes, this is a browser security hole, but I don't see how browser vendors can patch it.

Rodrigo
  • 4,706
  • 6
  • 51
  • 94
François Zaninotto
  • 7,068
  • 2
  • 35
  • 56
12

Merging various references found here in StackOverflow and in other sites, I did so using javascript and JQuery:

<html>
<body>
<canvas id="myCanvas" width="400" height="400" style="border:1px solid #c3c3c3;">
Your browser does not support the canvas element.
</canvas>
<script src="jquery.js"></script>
<script type="text/javascript">
    window.onload = function(){
        var canvas = document.getElementById('myCanvas');
        var context = canvas.getContext('2d');
        var img = new Image();
        img.src = 'photo_apple.jpg';
        context.drawImage(img, 0, 0);
    };

    function findPos(obj){
    var current_left = 0, current_top = 0;
    if (obj.offsetParent){
        do{
            current_left += obj.offsetLeft;
            current_top += obj.offsetTop;
        }while(obj = obj.offsetParent);
        return {x: current_left, y: current_top};
    }
    return undefined;
    }

    function rgbToHex(r, g, b){
    if (r > 255 || g > 255 || b > 255)
        throw "Invalid color component";
    return ((r << 16) | (g << 8) | b).toString(16);
    }

$('#myCanvas').click(function(e){
    var position = findPos(this);
    var x = e.pageX - position.x;
    var y = e.pageY - position.y;
    var coordinate = "x=" + x + ", y=" + y;
    var canvas = this.getContext('2d');
    var p = canvas.getImageData(x, y, 1, 1).data;
    var hex = "#" + ("000000" + rgbToHex(p[0], p[1], p[2])).slice(-6);
    alert("HEX: " + hex);
});
</script>
<img src="photo_apple.jpg"/>
</body>
</html>

This is my complete solution.. Here I only used canvas and one image, but if you need to use <map> over the image, it's possible too. I hope I have helped.

ebragaparah
  • 305
  • 4
  • 7
7

See new input[type=color] HTML5 element: http://www.w3.org/TR/html-markup/input.color.html, http://demo.hongkiat.com/html5-form-input-type/index2.html.

Now it works at least in Chrome (tested in Ubuntu, should work for Windows too). It launches color-select dialog provided by operating system. If there is an eyedropper in this dialog (it is for Gnome), then it's possible to pick a color from any point on your screen. Not cross-browser yet, but clean and standards-based.

tema
  • 81
  • 1
  • 1
  • Nice. Works in Firefox too, naturally, but it would be great if you could do this without having to pop up a browser dialog. – NoBugs Feb 07 '16 at 07:36
  • 3
    Unfortunately, Chrome recently updated their form control designs and their new color picker doesn't have an eyedropper. – mattsven Jul 13 '20 at 17:37
7

I do agree with the very detailed answer provided by Elijah. In addition, I would say that you don't need the canvas when it comes to images. As you stated yourself, you have those images available from within php and can do the color-query on the server.

I would suggest that you handle this problem with an external tool - this makes it even browser indepedent (but OS dependent): write a small tool (for instance in c#) which does the color query for you, is invoked with a shortcut and submits the color to your server. Make the tool available as download on your CMS.

Another aproach I used for a CMS was to "steal" colors by parsing the CSS: the use case was to make the colors of an already existing web site available as color palette to my application:

  • I asked the user to provide a URL from the target system - mostly the company's home page
  • I parsed the page to find all color definitions in all inline styles and linked styles
  • (you can easily extend this to all referenced images)
  • the result was a nice color palette with all coporate colors to choose from

Maybe that's also a solution for your CMS?

rdmueller
  • 10,742
  • 10
  • 69
  • 126
  • Cheers Ralf. Building a custom palette is a very nice idea, but not for my task at hand, as there will be a lot of new content with no defined colour range. A client side application is certainly the cleanest approach, but I need a tool that works both on Windows and Mac OS. It would be a nice reason to get back into client side programming again (and a first time on the Mac), but I have sooooo much to do right now. I think I am going with the image only approach. – Pekka Jan 07 '10 at 19:21
7

Chrome Canary now supports EyeDropper API!

UPDATE: Supported in Chrome 95+

https://www.chromestatus.com/feature/6304275594477568

I believe we can expect it soon in all popular browsers.

Basically it will change user's cursor to magnifying glass and lets him select any pixel on page (works with images, videos and iframes).

const eyeDropper = new EyeDropper()

async function useEyeDropper() {
  try {
    const selectedColor = await eyeDropper.open()
    console.log(selectedColor) // { sRGBHex: '#008080' }
  } catch (err) {
    console.log('eye dropper cancelled')
  }
}

someTriggerEl.addEventListener('click', () => {
  useEyeDropper();
})

If you don't understand async/await well:

const eyeDropper = new EyeDropper()

someTriggerEl.addEventListener('click', () => {
  eyeDropper.open().then(selectedColor => {
    console.log(selectedColor) // { sRGBHex: '#008080' }
  }).catch(() => {
    console.log('eye dropper cancelled')
  })
})

I'm so hyped about this feature

BorisTB
  • 1,686
  • 1
  • 17
  • 26
  • 1
    As of Chrome 95 this is live Great snippit [@BorisTB](https://stackoverflow.com/users/3739087/boristb) – kevin walker Oct 21 '21 at 07:40
  • 1
    today, the latest chrome on linux produces "rgba(172, 150, 118, 0)" where alpha channel is always zero, and not hex result. So you need some manual processing with the result. – sarkiroka Oct 14 '22 at 08:42
5

I don't know if this is feasible, but if your pages are static you could save an image screenshot of each one of them (or maybe one for each browser/screen resolution? ) and then use AJAX to send the cursor coordinates to the server and return the pixel color with PHP's imagecolorat().

To take the screenshots, you could use Selenium IDE like described here.

Hope it helps.

jbochi
  • 28,816
  • 16
  • 73
  • 90
3

To add to the previous answers --

One way of thinking of this problem is that you want to be able to do a screen-capture of a 1px by 1px region. A fairly common technique for capturing screen regions (for instance from within Web-based bug-reporting systems) is to use a signed Java applet and java.awt.Robot to capture the picture. If you sign the applet, your users will get a "do you trust this app" dialog (with an "always trust apps from this publisher" checkbox) and then will be able to use the tool.

You can then pass the result out to JavaScript using LiveConnect (the docs are old, but Java applets still support this), or you can post it to your server. Similarly, you can call into the Java applet from JavaScript.

  • The Java Applet angle is interesting. I am however not proficient in coding Applets, so I can't probably implement this for my task at hand, and will resort to the Canvas approach outlined above. However, thanks for bringing this to my attention, I may be able to do something with it at a later time. – Pekka Jan 07 '10 at 19:24
3

There is no built in DOM method to generically get the color of a DOM element (other than images or a <canvas>) at a particular pixel location.

Thus, in order to do this, we must use something like HTML2Canvas or DOM Panda to take a "screenshot" of our website, get the user's click location, and get the pixel color of the "screenshot" at that particular location.

Using HTML2Canvas (version 0.5.0-beta3) you can do something like this:

// Take "screenshot" using HTML2Canvas
var screenshotCanvas,
    screenshotCtx,
    timeBetweenRuns = 10,
    lastTime = Date.now();
function getScreenshot() {
    // Limit how soon this can be ran again
    var currTime = Date.now();
    if(currTime - lastTime > timeBetweenRuns) {
        html2canvas(document.body).then(function(canvas) {
            screenshotCanvas = canvas;
            screenshotCtx = screenshotCanvas.getContext('2d');
        });
        lastTime = currTime;
    }
}
setTimeout(function() { // Assure the initial capture is done
    getScreenshot();
}, 100);

// Get the user's click location
document.onclick = function(event) {
    var x = event.pageX,
        y = event.pageY;

    // Look what color the pixel at the screenshot is
    console.log(screenshotCtx.getImageData(x, y, 1, 1).data);
}


// Update the screenshot when the window changes size
window.onresize = getScreenshot;

Demo

Zach Saucier
  • 24,871
  • 12
  • 85
  • 147
  • Here's a demo implementing the above solution: https://jsfiddle.net/8850s/an9f24hp/ Click somewhere to set the `background-color` of the div in the top left (even on the div itself) – shreyasm-dev Jun 22 '20 at 16:50
3

I know this is a very old post but a new API call feature from google chrome came up called "eyeDropper"

this was taken from that blog

const eyeDropper = new EyeDropper();

try {
  const selectedColor = await eyeDropper.open();
  console.log(selectedColor);
  // logs: { sRGBHex: '#ff0000' }
} catch (err) {
  console.log("color selection cancelled");
}
//You can test for support of EyeDropper using the following snippet, if you’re browsing with a browser that supports the EyeDropper API a button will be rendered below the code snippet.

if ("EyeDropper" in window) {
  // supported
}

https://pqina.nl/blog/use-the-javascript-eye-dropper-api-to-select-colors-on-the-web/

Sir XtC
  • 166
  • 1
  • 9
3

As a security precaution you can't capture screen pixels with Javascript (so developers can't take snapshots of your personal data), but you CAN do it in Flash -- you can get pixel data within the Flash container using the flash.display.BitmapData class.

Check out http://www.sephiroth.it/tutorials/flashPHP/print_screen/ -- I've used it in Flash-based WYSYWIG projects to save images to a LAMP (PHP) server.

The problem with using Flash is that it's not natively supported on iOS devices, which are extremely popular now and worth developing for. Flash is on its way down the tubes.

The canvas-based method will certainly be a good one provided all of your visitors have up-to-date web browsers that support the canvas tag and JavaScript.

supershwa
  • 41
  • 1
1

I would like to add two important considerations on @BorrisTB's answer (and to everyone's answer pointing to the availability of EyeDropper API).

  1. We implemented Eyedropper API on a project which was running smoothly on my machine but was giving me EyeDropper is undefined on production server. Turns out it needed window.isSecureContext = true. Which is possible on localhost, and on HTTPS only. So make sure the page is loaded over HTTPS, for API to be available.
  2. At the moment it is only available on chromium based browsers and that too on Desktops only. Mozilla and Safari do not support this feature, and have no plan for supporting it for security reason. Please refer to the table below https://developer.mozilla.org/en-US/docs/Web/API/EyeDropper_API#browser_compatibility
Usman Shaukat
  • 1,315
  • 16
  • 22