Skip to content

Instantly share code, notes, and snippets.

@emeeks
Last active April 21, 2017 15:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save emeeks/5022696 to your computer and use it in GitHub Desktop.
Save emeeks/5022696 to your computer and use it in GitHub Desktop.
Slightly Random Colors

I think it may be more legible for information visualization to utilize a rougher specification for color and other element aspects. In this implementation, perturbations of color and line thickness provide a less uniform set of squares, but they are still nearly the same color. Perturbations of color, size, and line can all provide not only a nuanced aesthetic, but perhaps also convey a certain uncertainty in the data visualization. Using rgb specification, you imply a sort of precision similar to decimal precision in spatial coordinates, and by wiring your visualization to produce a slightly rougher color spectrum on output, you might correct for that. While line jitter has been a feature of drawing packages for some time, I haven't seen procedural application of this kind of perturbation to traditional data visualization.

I'm going to add some perturbation of paths following the same concept. I'll also put a few buttons in for user interaction.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<meta charset="utf-8">
<head>
<title>Color Perturbation for Information Visualization</title>
</head>
<style>
#colorviz {
width: 960px;
height: 960px;
}
</style>
<script src="http://d3js.org/d3.v3.min.js"></script>
<body>
<div><input type="color" onchange="changeColor(this.value)" name="colorselection">
<form>
Variation <input id="variationSlider" type="range" onchange="changeColor(currentColor)" min ="1" max="50" step ="1" value="15" />
<input type="text" id="variationInput" value="15" />
</form>
</div>
<div id="colorviz">
</div>
</body>
<script>
currentColor = "#40F065";
var width = 960,
height = 960;
var svg = d3.select("#colorviz").append("svg")
.attr("width", width)
.attr("height", height);
someData = [];
for (i=0;i<506;i++) {
someData.push(i);
}
svg.selectAll('rect')
.data(someData)
.enter()
.append('rect')
.attr("height",20)
.attr("width",20)
.attr("class","base")
.attr('x',function(d,i) {return i%22 * 20})
.attr('y',function(d,i) {return i%23 * 20})
.style("fill",function() {return lessSlightlyRandomColor(40,240,65,15)})
.style("stroke", function() {return lessSlightlyRandomColor(155,155,155,15)})
.style("stroke-width", function() {return slightlyRandomLineWidth(1,.95)})
svg
.append('rect')
.attr("height",80)
.attr("width",80)
.attr('x',450)
.attr('y',20)
.attr("class","not")
.style("fill", "rgb(40,240,65)")
.style("stroke", "rgb(155,155,155)")
.style("stroke-width", .45)
scaleRamp = d3.scale.linear().domain([0,9,19]).range(["#FF0000","#00FF00","#0000FF"]).clamp(true);
var someOtherData = [];
for (i=0;i<20;i++) {
someOtherData.push(i);
}
svg.selectAll('rect.ramp')
.data(someOtherData)
.enter()
.append("rect")
.attr("class","ramp")
.attr("height",20)
.attr("width",20)
.attr("y", 470)
.attr("x", function(d,i){ return i * 20})
.style("fill", function(d,i) { var rgbColor = fromHex(scaleRamp(i)); return lessSlightlyRandomColor(rgbColor[0],rgbColor[1],rgbColor[2],15)})
.style("cursor", "pointer")
.on("click", function(d) {changeColor(d3.select(this).style("fill"))})
function changeColor(colorPicked) {
currentColor = colorPicked;
var rgbColor = fromHex(colorPicked);
var newVariation = document.getElementById('variationSlider').value;
document.getElementById('variationInput').value = newVariation;
svg.select('rect.not')
.style("fill", colorPicked)
svg.selectAll('rect.base')
.style("fill",function() {return lessSlightlyRandomColor(rgbColor[0],rgbColor[1],rgbColor[2],newVariation)})
.style("stroke-width", function() {return slightlyRandomLineWidth(1,.95)})
svg.selectAll('rect.ramp')
.style("fill", function(d,i) { var rgbColor = fromHex(scaleRamp(i)); return lessSlightlyRandomColor(rgbColor[0],rgbColor[1],rgbColor[2],newVariation)})
}
function fromHex(hexInput) {
r = hexToR(hexInput);
g = hexToG(hexInput);
b = hexToB(hexInput);
function hexToR(h) {return parseInt((cutHex(h)).substring(0,2),16)}
function hexToG(h) {return parseInt((cutHex(h)).substring(2,4),16)}
function hexToB(h) {return parseInt((cutHex(h)).substring(4,6),16)}
function cutHex(h) {return (h.charAt(0)=="#") ? h.substring(1,7):h}
return [r,g,b];
}
function slightlyRandomColor(r,g,b,range) {
r = r + (Math.floor(Math.random() * range) - Math.floor(range / 2));
g = g + (Math.floor(Math.random() * range) - Math.floor(range / 2));
b = b + (Math.floor(Math.random() * range) - Math.floor(range / 2));
return "rgb("+r+","+g+","+b+")"
}
function lessSlightlyRandomColor(r,g,b,range) {
var scaleRamp = d3.scale.linear().domain([256,0]).range([.5,2]).clamp(true);
var rRange = (range * scaleRamp(r));
var gRange = (range * scaleRamp(g));
var bRange = (range * scaleRamp(b));
r = r + (Math.floor(Math.random() * rRange) - Math.floor(rRange / 2));
g = g + (Math.floor(Math.random() * gRange) - Math.floor(gRange / 2));
b = b + (Math.floor(Math.random() * bRange) - Math.floor(bRange / 2));
return "rgb("+r+","+g+","+b+")"
}
function slightlyRandomLineWidth(width,range) {
return width + ((Math.random() * range) - range/2);
}
</script>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment