Create a gist now

Instantly share code, notes, and snippets.

@emeeks /
Last active Dec 14, 2015

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="">
<meta charset="utf-8">
<title>Color Perturbation for Information Visualization</title>
#colorviz {
width: 960px;
height: 960px;
<script src=""></script>
<div><input type="color" onchange="changeColor(this.value)" name="colorselection">
Variation <input id="variationSlider" type="range" onchange="changeColor(currentColor)" min ="1" max="50" step ="1" value="15" />
<input type="text" id="variationInput" value="15" />
<div id="colorviz">
currentColor = "#40F065";
var width = 960,
height = 960;
var svg ="#colorviz").append("svg")
.attr("width", width)
.attr("height", height);
someData = [];
for (i=0;i<506;i++) {
.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)})
.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++) {
.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("fill"))})
function changeColor(colorPicked) {
currentColor = colorPicked;
var rgbColor = fromHex(colorPicked);
var newVariation = document.getElementById('variationSlider').value;
document.getElementById('variationInput').value = newVariation;'rect.not')
.style("fill", colorPicked)
.style("fill",function() {return lessSlightlyRandomColor(rgbColor[0],rgbColor[1],rgbColor[2],newVariation)})
.style("stroke-width", function() {return slightlyRandomLineWidth(1,.95)})
.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);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment