Skip to content

Instantly share code, notes, and snippets.

@Explosion-Scratch
Last active May 6, 2023 04:47
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Explosion-Scratch/4c787653703cc9b3d418f19fe621d38d to your computer and use it in GitHub Desktop.
Save Explosion-Scratch/4c787653703cc9b3d418f19fe621d38d to your computer and use it in GitHub Desktop.
Shows a number in the favicon like Discord and Gmail do.
/**
* Generates a data URL of the current favicon with a number added on top.
* @param {Object} options
* @param {String} [options.type = "image/png"] The mime type of the image to return.
* @param {String} [options.text = ""] The text to display on the favicon, if left blank will simply show a dot on the favicon.
* @param {String} [options.background = "white"] A CSS color for the background of the notification badge.
* @param {String} [options.color = "white"] A CSS color for the color of the text on the notification badge.
* @param {Number} [options.size = 10] The size of the notification badge. The badge generated will be size * 2 pixels in width and height, then added on top of the current favicon.
* @param {String} [options.pos = "bottom-right"] The position of the badge, either "bottom-right", "top-right", "bottom-left" or "top-left"
* @param {String} [options.font = "Monospace"] The font to use
* @param {String} [options.iconUrl] The URL of the base icon, if not provided will be the current favicon.
* @returns {Promise.<string>} Returns a promise that resolves into the data URL of the icon generated.
* @example
* getIcon({
* text: "1",
* pos: "top-right",
* }).then((data_url) => {
* document.querySelector("link[rel='icon']").href = data_url;
* });
*/
async function getIcon({
type = "image/png",
text = "",
background = "white",
color = "black",
size = 10,
pos = "bottom-right",
font = "Monospace",
iconUrl
}) {
const icon = iconUrl || document.querySelector("link[rel='icon']")?.href;
let data = await getData(icon);
let canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
let img = await loadedImg(data);
let notif_img = await loadedImg(getNotifData(text, size));
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
let x = 0;
let y = 0;
let sections = pos.trim().toLowerCase().split("-");
sections[0] === "bottom" && (y = canvas.height - size * 2);
sections[1] == "right" && (x = canvas.width - size * 2);
ctx.drawImage(notif_img, x, y);
return canvas.toDataURL(type);
function loadedImg(src) {
return new Promise((res) => {
let img = new Image();
img.src = src;
img.onload = () => res(img);
});
}
function getNotifData(text, size) {
const _canvas = document.createElement("canvas");
_canvas.width = size * 2;
_canvas.height = size * 2;
let c = _canvas.getContext("2d");
const inset = size;
c.beginPath();
c.fillStyle = background;
c.arc(inset, inset, size, 0, 2 * Math.PI);
c.fill();
c.font = `${size * 1.5}px ${font}`;
c.fillStyle = color;
c.textBaseline = "top";
c.fillText(text, inset / 2, inset / 2);
return _canvas.toDataURL("image/png");
}
function getData(url) {
return new Promise(async (res, reject) => {
let blob = await fetch(url).then((r) => r.blob());
let dataUrl = await new Promise((resolve) => {
let reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.readAsDataURL(blob);
});
res(dataUrl);
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment