diff --git a/charts/beeswarmPlot.js b/charts/beeswarmPlot.js
new file mode 100644
index 00000000..7bf5c580
--- /dev/null
+++ b/charts/beeswarmPlot.js
@@ -0,0 +1,289 @@
+(function() {
+
+ // A multiple beeswarm plot
+
+ // The Model
+ // The model abstraction is a matrix of categories: the main dimansion will define the groups,
+ // and the secondary will define the single bars.
+ // Optional dimension is on the bar chart color (to be defined).
+
+ var model = raw.model();
+
+ // Group dimension.
+ // It can accept both numbers and strings
+ var groups = model.dimension()
+ .title('Groups')
+ .types(Number, String)
+
+ // values dimension. each category will define a bar
+ // It can accept both numbers and strings
+ var values = model.dimension()
+ .title('X Axis')
+ .types(Number, Date)
+ .required(true)
+
+ // Values dimension. It will define the height of the bars
+ var radiuses = model.dimension()
+ .title('Radius')
+
+ // Colors dimension. It will define the color of the bubbles
+ var colorsDimesion = model.dimension()
+ .title('Colors')
+ .types(Number, String)
+
+ // Values dimension. It will define the height of the bars
+ var labels = model.dimension()
+ .title('Labels')
+
+ model.map(function(data) {
+
+ var results = d3.nest()
+ .key(function(d) { return d[groups()] })
+ .entries(data)
+
+ // remap the array
+ results.forEach(function(d) {
+ d.values = d.values.map(function(g) {
+ return {
+ group: g[groups()],
+ value: values.type() == 'Date' ? new Date(g[values()]) : +g[values()],
+ radius: radiuses() ? +g[radiuses()] : 1,
+ color: colorsDimesion() ? g[colorsDimesion()] : null,
+ label: g[labels()]
+ }
+ })
+ })
+
+ return results;
+ })
+
+
+ // The Chart
+ var chart = raw.chart()
+ .title('Beeswarm Plot')
+ .description("It distributes elements horizontally avoiding overlap between them and according to a selected dimension.
Based on https://bl.ocks.org/mbostock/6526445e2b44303eebf21da3b6627320")
+ .thumbnail("imgs/beeswarm.png")
+ .category('Dispersion')
+ .model(model)
+
+ // visualiziation options
+ // Width
+ var width = chart.number()
+ .title('Width')
+ .defaultValue(800)
+
+ // Height
+ var height = chart.number()
+ .title('Height')
+ .defaultValue(600)
+
+ // Spatialization iterations
+ var anticollisionIterations = chart.number()
+ .title('Anticollision iterations')
+ .defaultValue(1);
+
+ var marginCircles = chart.number()
+ .title("Circles padding")
+ .defaultValue(.5);
+
+ // Space between barcharts
+ var padding = chart.number()
+ .title('Vertical padding')
+ .defaultValue(0);
+
+ var minRadius = chart.number()
+ .title("min radius")
+ .defaultValue(2);
+
+ var maxRadius = chart.number()
+ .title("max radius")
+ .defaultValue(20);
+
+ var sorting = chart.list()
+ .title("Sort by")
+ .values(['Original', 'Name (ascending)', 'Name (descending)', 'Total (descending)', 'Total (ascending)'])
+ .defaultValue('Original')
+
+ // Chart colors
+ var colors = chart.color()
+ .title("Color scale")
+
+ // Drawing function
+ // selection represents the d3 selection (svg)
+ // data is not the original set of records
+ // but the result of the model map function
+ chart.draw(function(selection, data) {
+
+ console.log(data)
+
+ //sort data
+ function sortBy(a, b) {
+ if (sorting() == 'Name (descending)') {
+ if (a.key < b.key) return 1;
+ if (a.key > b.key) return -1;
+ return 0;
+ } else if (sorting() == 'Name (ascending)') {
+ if (a.key < b.key) return -1;
+ if (a.key > b.key) return 1;
+ return 0;
+ } else if (sorting() == 'Total (descending)') {
+ if (a.values.length < b.values.length) return 1;
+ if (a.values.length > b.values.length) return -1;
+ return 0;
+ } else if (sorting() == 'Total (ascending)') {
+ if (a.values.length < b.values.length) return -1;
+ if (a.values.length > b.values.length) return 1;
+ return 0;
+ }
+ }
+ data.sort(sortBy);
+
+ // Define margins
+ var margin = { top: 50, right: 25, bottom: 0, left: 25 };
+
+ //define title space
+ var titleSpace = groups() == null ? 0 : 30;
+
+ // Define common variables.
+
+ // svg size
+ selection
+ .attr("width", width())
+ .attr("height", height())
+
+ // define single beswarm plot height, depending on the number of bar charts
+ var w = +width() - margin.left - margin.right,
+ h = (+height() - margin.bottom - margin.top - ((titleSpace + padding()) * (data.length - 1))) / data.length;
+
+ // Define scales
+
+ // Radiuses
+ let rMax = d3.max(data, function(d) {
+ return d3.max(d.values, function(e) {
+ return e.radius;
+ })
+ })
+ let rMin = d3.min(data, function(d) {
+ return d3.min(d.values, function(e) {
+ return e.radius;
+ })
+ })
+ var radius = d3.scaleLinear()
+ .range([minRadius(), maxRadius()])
+ .domain([rMin, rMax])
+
+ // colors
+ let allColors = [];
+ data.forEach(function(d) {
+ d.values.forEach(function(dd) {
+ allColors.push(dd);
+ })
+ })
+ allColors = d3.nest()
+ .key(function(d) { return d.color; })
+ .entries(allColors)
+ .map(function(d) {
+ return d.key
+ })
+ colors.domain(allColors);
+
+ // Horizontal
+
+ //handle data type (Number or Date)
+ var xScale = values.type() == 'Date' ? d3.scaleTime() : d3.scaleLinear();
+
+ let xMax = d3.max(data, function(d) {
+ return d3.max(d.values, function(e) {
+ return e.value;
+ })
+ })
+ let xMin = d3.min(data, function(d) {
+ return d3.min(d.values, function(e) {
+ return e.value;
+ })
+ })
+ // if (values.type() == 'Date') {
+ // xMin = new Date(xMin);
+ // xMax = new Date(xMax)
+ // }
+
+ xScale.range([radius(radius.domain()[0]), w - radius(radius.domain()[1])])
+ .domain([xMin, xMax]);
+
+ console.log(xScale.domain())
+
+ // Draw each bar chart
+ data.forEach(function(item, index) {
+
+ // Append a grupo containing axis and circles,
+ // move it according the index
+ let beeswarm = selection.append("g")
+ .attr("transform", "translate(" + margin.left + "," + index * (h + padding() + titleSpace) + ")");
+
+ // Draw title
+ beeswarm.append("text")
+ .attr("x", -margin.left)
+ .attr("y", titleSpace - 7)
+ .style("font-size", "10px")
+ .style("font-family", "Arial, Helvetica")
+ .text(item.key);
+
+ let data = item.values;
+
+ var simulation = d3.forceSimulation(data)
+ .force("x", d3.forceX(function(d) {
+ return xScale(d.value) }
+ )
+ .strength(1))
+ .force("y", d3.forceY(h / 2))
+ .force("collide", d3.forceCollide(function(d) { return radius(d.radius) + marginCircles() }).iterations(anticollisionIterations()))
+ .stop();
+
+ for (var i = 0; i < 240; ++i) simulation.tick();
+
+ let bees = beeswarm.append('g')
+ .attr('class', 'bees')
+ .selectAll("circle")
+ .data(data).enter()
+ .append('circle')
+ .attr('r', function(d) { return radius(d.radius) })
+ .attr('cx', function(d) { return d.x })
+ .attr('cy', function(d) { return d.y })
+ .attr("fill", function(d) {
+ if (d.color) {
+ return colors()(d.color);
+ } else {
+ return '#444'
+ }
+ });
+
+ let labels = beeswarm.append('g')
+ .attr('class', 'label')
+ .selectAll("text")
+ .data(data).enter()
+ .append('text')
+ .attr('x', function(d) { return d.x })
+ .attr('y', function(d) { return d.y })
+ .attr('text-anchor','middle')
+ .attr('fill', '#000')
+ .text(function(d){ if (d.label) return d.label; })
+
+
+ })
+
+ // After all the charts, draw x axis
+ selection.append("g")
+ .attr("class", "x axis")
+ .style("font-size", "10px")
+ .style("font-family", "Arial, Helvetica")
+ .attr("transform", "translate(" + margin.left + "," + ((h + padding() + titleSpace) * data.length - padding()) + ")")
+ .call(d3.axisBottom(xScale));
+
+ // Set styles
+ d3.selectAll(".axis line, .axis path")
+ .style("shape-rendering", "crispEdges")
+ .style("fill", "none")
+ .style("stroke", "#ccc");
+
+ })
+})();
\ No newline at end of file
diff --git a/imgs/beeswarm.png b/imgs/beeswarm.png
new file mode 100644
index 00000000..bd6d440e
Binary files /dev/null and b/imgs/beeswarm.png differ
diff --git a/index.html b/index.html
index d93cd835..cf966a6a 100644
--- a/index.html
+++ b/index.html
@@ -142,6 +142,7 @@
+