<!DOCTYPE html>
<meta charset=utf-8>
<body style="background:#cff">
<p>
Semitransparent cursor test. The gradients blend from opaque to clear. The arrow cursors are opaque. Mouseover or download cur file.

<p><a href="https://connect.microsoft.com/IE/feedback/details/781016/im-unable-to-create-cursors-using-a-data-uri">IE 10 does not support data uris for cursors</a> or in links, so download these cursors and test within  in Windows. Or just test in other browser within Windows.
<script>
// reference: the old new thing 4-part series http://blogs.msdn.com/b/oldnewthing/archive/2010/10/18/10077133.aspx
if (true) (function() {
        var canvas = document.createElement("canvas");
        canvas.width = canvas.height = 32;
        var c = canvas.getContext("2d");
        var grad = c.createLinearGradient(0,0,32,32);
        grad.addColorStop(0, 'blue');
        grad.addColorStop(1, 'rgba(0,255,0,0)');
        c.fillStyle = grad;
        c.fillRect(0,0,32,32);
        addIconToBody(canvas, "gradient", 0, 0);
})();
if (true) (function() {
        var canvas = document.createElement("canvas");
        canvas.width = canvas.height = 32;
        var c = canvas.getContext("2d");
        var grad = c.createLinearGradient(32,32,0,0);
        grad.addColorStop(0, 'blue');
        grad.addColorStop(1, 'rgba(0,255,0,0)');
        c.fillStyle = grad;
        c.fillRect(0,0,32,32);
        addIconToBody(canvas, "upsidedowngradient", 31, 31);
})();
if (true) (function() {
        var canvas = document.createElement("canvas");
        canvas.width = canvas.height = 32;
        var c = canvas.getContext("2d");
        var grad = c.createLinearGradient(0,0,32,0);
        grad.addColorStop(0, 'blue');
        grad.addColorStop(1, 'rgba(0,255,0,0)');
        c.fillStyle = grad;
        c.fillRect(0,0,32,32);
        addIconToBody(canvas, "righttoleftgradient", 0, 0);
})();
addTriangleIcon("red", "red");
addTriangleIcon("green", "green");
addTriangleIcon("blue", "blue");
function addTriangleIcon(color, name) {
        var canvas = document.createElement("canvas");
        canvas.width = canvas.height = 32;
        var c = canvas.getContext("2d");
        c.fillStyle = color;
        c.beginPath();
        c.moveTo(0,0);
        c.lineTo(0,32);
        c.lineTo(20,25);
        c.closePath();
        c.fill();
        addIconToBody(canvas, name, 0, 0);
}

function addIconToBody(canvas, title, hotspotX, hotspotY) {
        var iconBuffer = toIcon(canvas, hotspotX, hotspotY);
        var a = document.createElement("a");
        // what's the right mime type? image/x-icon image/x-win-bitmap application/cur
        var iconBufferBase64 = btoa(String.fromCharCode.apply(null, new Uint8Array(iconBuffer)));
        var url = "data:image/x-icon;base64," + iconBufferBase64;
        var pngurl = canvas.toDataURL();
        a.href = url;
        a.download = title + ".cur";
        a.textContent = title + ".cur";
        var div = document.createElement("div");
        var cssCursor =
                "url('" + url + "'), " + 
                //"url(" + pngurl + ") " + hotspotX + " " + hotspotY + ", " +
                "auto";
        div.style.cursor = cssCursor;
        div.appendChild(canvas)
        div.appendChild(a);
        document.body.appendChild(div)
}

/** Convert any canvas into a cursor.
 * TODO: throw errors when the canvas is too big.
 * TODO: allow specifying AND mask.
 */
function toIcon(canvas, hotspotX, hotspotY) {
        var c = canvas.getContext("2d");
        var imageData = c.getImageData(0,0,canvas.width, canvas.height);
        imageData.data  // rgba uint8clampedarray
        var icondirentrySize = 1+1+1+1 + 2+2 + 4+4;
        var icondirSize = 4 + 4 + 4 + 1*icondirentrySize;
        var bitmapinfoheaderSize = 4*3 + 2+2 + 4*6;
        var iconimageSize = bitmapinfoheaderSize +
                0 +
                4*imageData.width*imageData.height +
                Math.floor((imageData.width+31)/32)*4*imageData.height;
        var iconBuffer = new ArrayBuffer(icondirSize + iconimageSize);
        var iconimageOffset = icondirSize;

        var iconDirDataView = new DataView(iconBuffer, 0, icondirSize);
        fillIconDir(iconDirDataView, imageData, iconimageSize, iconimageOffset, hotspotX, hotspotY);

        var iconDirDataView = new DataView(iconBuffer, iconimageOffset, iconimageSize);
        fillIconImage(iconDirDataView, imageData);
        return iconBuffer;
}
function fillIconDir(dv, imageData, iconimageSize, iconimageOffset, hotspotX, hotspotY) {
        dv.setUint16(0, 0, true);  // idReserved=0
        dv.setUint16(2, 2, true);  // idType=2 for cursors, 1 for icons
        dv.setUint16(4, 1, true);  // idCount of images
        fillIconDirEntry(new DataView(dv.buffer, dv.byteOffset+6, dv.byteLength-6), imageData, iconimageSize, iconimageOffset, hotspotX, hotspotY);
}
function fillIconDirEntry(dv, imageData, iconimageSize, iconimageOffset, hotspotX, hotspotY) {
        dv.setUint8(0, imageData.width);
        dv.setUint8(1, imageData.height);
        dv.setUint8(2, 0);  // bColorCount of color table is 0 for 32bpp
        dv.setUint8(3, 0);
        if (true) {  // cursor hotspot from upper left
                // TODO: get reference for this
                dv.setUint16(4, hotspotX, true);
                dv.setUint16(6, hotspotY, true);
        } else {  // icon
                dv.setUint16(4, 1, true);  // wPlanes = 1; it was used for EGA displays; see old new thing.
                dv.setUint16(6, 32, true); // wBitCount
        }
        dv.setUint32(8, iconimageSize, true);
        dv.setUint32(12, iconimageOffset, true);
}

function fillIconImage(dv, imageData) {
        var bitmapinfoheaderSize = 4*3 + 2+2 + 4*6;
        dv.setUint32(0, bitmapinfoheaderSize, true);
        dv.setInt32(4, imageData.width, true);
        dv.setInt32(8, imageData.height*2, true);  // biHeight of combined masks, positive for bottom-up.
        dv.setInt16(12, 1, true);  // biPlanes=1; it was used for EGA displays; see old new thing.
        dv.setInt16(14, 32, true);  // biBitCount
        dv.setUint32(16, 0, true);  // biCompression=BI_RGB (0)
        var biSizeImage =
                4*imageData.width*imageData.height +
                Math.floor((imageData.width+31)/32)*4*imageData.height;
        dv.setUint32(20, biSizeImage, true);  // biSizeImage. I think this should include both AND and XOR.images.
        dv.setInt32(24, 0, true);
        dv.setInt32(28, 0, true);
        dv.setUint32(32, 0, true);
        dv.setUint32(36, 0, true);

        // XOR mask, 32bpp ARGB bottom-up
        // note that imageData.data is RGBA top-down.
        var xorBegin = 40;
        var andBegin = xorBegin + 4*imageData.width*imageData.height;
        var xorRow = xorBegin + 4*imageData.width*(imageData.height-1);
        var andRow = andBegin + Math.floor((imageData.width+31)/32)*4*(imageData.height-1);
        var imageDataRow = 0;
        for (var y = 0; y < imageData.width; y++) {
                var andBit = 0x80;
                for (var x = 0; x < imageData.height; x++) {
                        var imageDataPos = imageDataRow + 4*x;
                        var xorPos = xorRow + 4*x;
                        var andByte = andRow + Math.floor(x/8);
                        var
                                r = imageData.data[imageDataPos + 0],
                                g = imageData.data[imageDataPos + 1],
                                b = imageData.data[imageDataPos + 2],
                                a = imageData.data[imageDataPos + 3];
                        dv.setUint8(xorPos + 0, b);
                        dv.setUint8(xorPos + 1, g);
                        dv.setUint8(xorPos + 2, r);
                        dv.setUint8(xorPos + 3, a);
                        var showBackground = a < 0x80;
                        dv.setUint8(andByte, dv.getUint8(andByte) | (showBackground?andBit:0));
                        andBit = (andBit >> 1 | andBit << 7) & 0xff;
                }
                xorRow -= 4*imageData.width;
                andRow -= Math.floor((imageData.width+31)/32)*4;
                imageDataRow += 4*imageData.width;
        }
}


/**
typedef struct
{
    WORD           idReserved;   // Reserved (must be 0)
    WORD           idType;       // Resource Type (1 for icons)
    WORD           idCount;      // How many images?
    ICONDIRENTRY   idEntries[1]; // An entry for each image (idCount of 'em) Q: is there padding before this? I don't thinkso.
} ICONDIR, *LPICONDIR;
 */

/**
typedef struct
{
    BYTE        bWidth;          // Width, in pixels, of the image
    BYTE        bHeight;         // Height, in pixels, of the image
    BYTE        bColorCount;     // Number of colors in image (1<<(wBitCount*wPlanes), or 0 if >=8bpp (wBitCount*wPlanes>=8))
    BYTE        bReserved;       // Reserved ( must be 0)
    WORD        xhotspot // ico: wPlanes;         // Color Planes
    WORD        yhotspot from top // ico: wBitCount;       // Bits per pixel
    DWORD       dwBytesInRes;    // How many bytes in this resource?
    DWORD       dwImageOffset;   // Where in the file is this image?
} ICONDIRENTRY, *LPICONDIRENTRY;

typdef struct
{
   BITMAPINFOHEADER   icHeader;      // DIB header
   RGBQUAD         icColors[1];   // Color table; empty for biBitCount=32
   BYTE            icXOR[1];      // DIB bits for XOR mask (0RGB, or ARGB). DIBs are bottom-up.
   BYTE            icAND[1];      // DIB bits for AND mask. all rows padded to 32 bits (i.e. row size = (biBitCount*biWidth+31)/32*4).
} ICONIMAGE, *LPICONIMAGE;

// (device independent bitmap)
typedef struct tagBITMAPINFOHEADER {
  DWORD biSize;                   // sizeof of this header
  LONG  biWidth;                  // The width of the bitmap, in pixels.
  LONG  biHeight;                 // COMBINED height of XOR and AND pixels of icon. Positive for bottom-up, negative for top-down
  WORD  biPlanes;                 // must be 1
  WORD  biBitCount;               // bits per pixel e.g. 32
  DWORD biCompression;            // e.g. PNG but must be BI_RGB=0 for icon? (or BI_BITFIELDS=3 according to old new thing?)
  DWORD biSizeImage;              // byte size; may be 0 for BI_RGB
  LONG  biXPelsPerMeter;          // must be 0
  LONG  biYPelsPerMeter;          // must be 0
  DWORD biClrUsed;                // must be 0?
  DWORD biClrImportant;           // must be 0?
} BITMAPINFOHEADER, *PBITMAPINFOHEADER;

*/
</script>