Hello everyone,
I was trying to download my Codeforces rating graph by right clicking on it and using "Save image as...".
But the downloaded image was completely blank.

At first I thought it was a browser issue, so I inspected the graph.
I ran:
document.querySelectorAll("#usersRatingGraphPlaceholder canvas")
and got:
NodeList(2) [
canvas.flot-base,
canvas.flot-overlay
]
Then I checked which element is actually on top of the graph:
document.elementFromPoint(300, 250)
It returned:
<canvas class="flot-overlay" ...></canvas>
So when I right click inside the graph, the element being targeted is the flot-overlay canvas.
This seems to be why "Save image as..." saves a blank image instead of the visible rating graph.
I then tried exporting flot-base directly. This gave me the actual graph, but the rating values on the left and the dates at the bottom were missing.
After inspecting the graph structure, I found this:
canvas.flot-base
div.flot-text
canvas.flot-overlay
div.legend
The axis text is rendered separately inside flot-text, so exporting only the base canvas does not include it.
I made a small PoC to export the base canvas and draw the text labels on a new canvas:
function downloadRatingGraph() {
const container = document.querySelector(
"#usersRatingGraphPlaceholder"
);
if (!container) return;
const base = container.querySelector(".flot-base");
if (!base) return;
const canvas = document.createElement("canvas");
canvas.width = base.width;
canvas.height = base.height;
const ctx = canvas.getContext("2d");
ctx.fillStyle = "#fff";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(base, 0, 0);
const containerRect = container.getBoundingClientRect();
const scaleX = base.width / containerRect.width;
const scaleY = base.height / containerRect.height;
container
.querySelectorAll(".flot-text div")
.forEach(element => {
if (element.children.length > 0) return;
const text = element.textContent.trim();
if (!text) return;
const rect = element.getBoundingClientRect();
const style = getComputedStyle(element);
const x =
(rect.left - containerRect.left) * scaleX;
const y =
(rect.top - containerRect.top) * scaleY;
const fontSize =
parseFloat(style.fontSize) * scaleY;
ctx.font =
`$$${fontSize}px $$${style.fontFamily}`;
ctx.fillStyle = style.color || "#000";
ctx.textBaseline = "top";
ctx.fillText(text, x, y);
});
const link = document.createElement("a");
link.download = "rating-graph.png";
link.href = canvas.toDataURL("image/png");
link.click();
}
After running:
downloadRatingGraph();
I was able to download the graph with the rating values and date labels.

This is just a small PoC and there may be a cleaner solution in Flot itself.
Note: AI was used during debugging and for helping with the PoC. I manually tested the code and verified the results myself.
Thanks :)







