Для того, чтобы слова не обрезались и не скучивались где-то в уголке, можно использовать такой приём: пусть центр каждого тега не появляется в 25% (от ширины/высоты) от края.
Цвета генерировать можно, например, на основе оттенка H в модели HSL зависящего от популярности тега. (т. е. яркость и насыщенность остаются равномерными)
Менее популярные теги сложно прочитать, поскольку они очень мелкие. Я думаю, что популярность тега можно выразить не через размер, а через жирность начертания шрифта. Тогда размер, например, может колебаться от 1.5em до 4em (что подходит для чтения). А для подчёркивания популярности использовать жирность шрифта (font weight, обозначается как "wght") от 100 до 900, что даёт более широкий набор возможностей, какие стили применить. (Например, более жирный текст отбрасывает более крупную тень, которую, если подкрасить, можно в данном случае воспринять так, что более популярные теги ярче светятся)
P. S. Для примера я использовал шрифт
https://v-fonts.com/fonts/ff-meta-variable, я подключил его через CORS Proxy, чтобы были правильные заголовки, вы же можете скачать его к себе, или какой-либо другой переменный шрифт. Обратите внимание, что это должен быть именно переменный шрифт, только тогда дробные жирности будут работать. Если вам интересна эта тема, можете посмотреть, что вообще возможно при помощи переменных шрифтов
https://v-fonts.com/
Я использовал проценты, так что #tagContainer может быть любого размера.
<!DOCTYPE html>
<html>
<head>
<title></title>
<style>
@font-face {
src: url("https://cors-anywhere.herokuapp.com/https://v-fonts.com/assets/fonts/MetaVariableDemo-Set.woff2");
font-family: "FF Meta Variable";
font-style: normal;
}
body {
margin: 0;
}
#tagContainer {
position: relative;
width: 100vw;
height: 100vh;
font-family: "FF Meta Variable";
overflow: hidden;
margin: 0;
background-color: black;
text-shadow: 0 0 2em;
}
#tagContainer > * {
position: absolute;
mix-blend-mode: difference;
mix-blend-mode: screen;
transform: translate(-50%, -50%);
}
</style>
<script>
addEventListener("load", event => {
var cloudElement = document.getElementById("tagContainer");
var tags = [
["Javascript", 1634, 987],
["jQuery", 1111, 34],
["PHP", 1024, 1122],
["Asp.Net", 977, 1005],
["Photoshop", 594, 789],
["XML", 40, 666],
["Access", 55, 77],
["Java", 278, 277],
["MySQL", 155, 122]
];
const { min, max } = tags.reduce(({ min, max }, [, ...v]) => {
const total = v.reduce((a, b) => a + b, 0);
return {
min: Math.min(min, total),
max: Math.max(max, total)
}
}, { min: Infinity, max: -Infinity });
tags.forEach(([tag, ...v]) => {
const total = v.reduce((a, b) => a + b, 0);
const p = (total - min) / (max - min);
const element = document.createElement("span");
element.style.cssText = `
font-variation-settings: "wght" ${100 + 800 * p};
font-size: ${1.5 + 2.5 * p}em;
color: hsl(${180 + 960 * p}, 100%, 50%);
top: ${25 + 50 * Math.random()}%;
left: ${25 + 50 * Math.random()}%;
`;
element.textContent = tag;
cloudElement.append(element);
})
});
</script>
</head>
<body>
<div id="tagContainer"></div>
</body>
</html>