xkcd-style Plots

The code below attempts to apply the XKCD style to a variety of plots and charts. The idea is to first apply cartoon-like styles to the graphics objects (thick lines, silly font etc), and then to apply a distortion using image processing.

The final function is xkcdConvert which is simply applied to a standard plot or chart.

The font style and size are set by xkcdStyle which can be changed to your preference. I've used the dreaded Comic Sans font, as the text will get distorted along with everything else and I thought that starting with the Humor Sans font might lead to unreadable text.

The function xkcdLabel is provided to allow labelling of plot lines using a little callout. The usage is xkcdLabel[{str,{x1,y1},{xo,yo}] where str is the label (e.g. a string), {x1,y1} is the position of the callout line and {xo,yo} is the offset determining the relative position of the label. The first example demonstrates its usage.

xkcdStyle = {FontFamily -> "Comic Sans MS", 16};

xkcdLabel[{str_, {x1_, y1_}, {xo_, yo_}}] := Module[{x2, y2},
   x2 = x1 + xo; y2 = y1 + yo;
   {Inset[
     Style[str, xkcdStyle], {x2, y2}, {1.2 Sign[x1 - x2], 
      Sign[y1 - y2] Boole[x1 == x2]}], Thick, 
    BezierCurve[{{0.9 x1 + 0.1 x2, 0.9 y1 + 0.1 y2}, {x1, y2}, {x2, y2}}]}];

xkcdRules = {EdgeForm[ef:Except[None]] :> EdgeForm[Flatten@{ef, Thick, Black}], 
   Style[x_, st_] :> Style[x, xkcdStyle], 
   Pane[s_String] :> Pane[Style[s, xkcdStyle]],
   {h_Hue, l_Line} :> {Thickness[0.02], White, l, Thick, h, l},
   Grid[{{g_Graphics, s_String}}] :> Grid[{{g, Style[s, xkcdStyle]}}],
   Rule[PlotLabel, lab_] :> Rule[PlotLabel, Style[lab, xkcdStyle]]};

xkcdShow[p_] := Show[p, AxesStyle -> Thick, LabelStyle -> xkcdStyle] /. xkcdRules

xkcdShow[Labeled[p_, rest__]] := 
 Labeled[Show[p, AxesStyle -> Thick, LabelStyle -> xkcdStyle], rest] /. xkcdRules

xkcdDistort[p_] := Module[{r, ix, iy},
   r = ImagePad[Rasterize@p, 10, Padding -> White];
   {ix, iy} = 
    Table[RandomImage[{-1, 1}, ImageDimensions@r]~ImageConvolve~
      GaussianMatrix[10], {2}];
   ImagePad[ImageTransformation[r, 
     # + 15 {ImageValue[ix, #], ImageValue[iy, #]} &, DataRange -> Full], -5]];

xkcdConvert[x_] := xkcdDistort[xkcdShow[x]]

Version 7 users will need to use this code for xkcdDistort:

xkcdDistort[p_] := 
 Module[{r, id, ix, iy, samplepoints, funcs, channels},
  r = ImagePad[Rasterize@p, 10, Padding -> White]; 
  id = Reverse@ImageDimensions[r];
  {ix, iy} = Table[ListInterpolation[ImageData[
      Image@RandomReal[{-1, 1}, id]~ImageConvolve~GaussianMatrix[10]]], {2}]; 
  samplepoints = Table[{x + 15 ix[x, y], y + 15 iy[x, y]}, {x, id[[1]]}, {y, id[[2]]}]; 
  funcs = ListInterpolation[ImageData@#] & /@ ColorSeparate[r]; 
  channels = Apply[#, samplepoints, {2}] & /@ funcs; 
  ImagePad[ColorCombine[Image /@ channels], -10]]

Examples

Standard Plot including xkcdLabel as an Epilog:

f1[x_] := 5 + 50 (1 + Erf[x - 5]);
f2[x_] := 20 + 30 (1 - Erf[x - 5]);
xkcdConvert[Plot[{f1[x], f2[x]}, {x, 0, 10},
  Epilog -> 
   xkcdLabel /@ {{"Label 1", {1, f1[1]}, {1, 30}}, {"Label 2", {8, f2[8]}, {0, 30}}},
  Ticks -> {{{3.5, "1st Event"}, {7, "2nd Event"}}, Automatic}]]

enter image description here

BarChart with either labels or legends:

xkcdConvert[BarChart[{10, 1}, ChartLabels -> {"XKCD", "Others"},
  PlotLabel -> "Popularity of questions on MMA.SE",
  Ticks -> {None, {{1, "Min"}, {10, "Max"}}}]]

enter image description here

xkcdConvert[BarChart[{1, 10}, ChartLegends -> {"Others", "XKCD"},
  PlotLabel -> "Popularity of questions on MMA.SE",
  ChartStyle -> {Red, Green}]]

enter image description here

Pie chart:

xkcdConvert[PieChart[{9, 1}, ChartLabels -> {"XKCD", "Others"},
  PlotLabel -> "Popularity of questions on MMA.SE"]]

enter image description here

ListPlot:

xkcdConvert[
 ListLinePlot[RandomInteger[10, 15], PlotMarkers -> Automatic]]

enter image description here

3D plots:

xkcdConvert[BarChart3D[{3, 2, 1}, ChartStyle -> Red, FaceGrids -> None,
  Method -> {"Canvas" -> None}, ViewPoint -> {-2, -4, 1},
  PlotLabel -> "This is just silly"]]

enter image description here

xkcdConvert[
 Plot3D[Exp[-10 (x^2 + y^2)^4], {x, -1, 1}, {y, -1, 1}, 
  MeshStyle -> Thick,
  Boxed -> False, Lighting -> {{"Ambient", White}},
  PlotLabel -> Framed@"This plot is not\nparticularly useful"]]

enter image description here

It should also work for various other plotting functions like ParametricPlot, LogPlot and so on.


Mostly thanks to Belisarius's elegant wrapping, you can do

h[fun_, divisor_, color_, at_] := Module[{k},
   k = BSplineFunction[Table[fun@x + RandomReal[{-0.1, 0.1}/divisor], {x, 0.01, 10, .1}]];
   ParametricPlot[k[x], {x,0.1,0.9}, PlotStyle->{color, AbsoluteThickness@at}, Axes-> None]];

Show[{
  h[{#, 1.5 + 10 (Sin[#]^2/Sqrt[#]) Exp[-(# - 5)^2/2]} &, 3, Darker[Cyan, 0.3],  3],
  h[{#, 3 + 10 (Sin[#]^2/Sqrt[#])   Exp[-(# - 7)^2/2]} &, 3, White,              8],
  h[{#, 3 + 10 (Sin[#]^2/Sqrt[#])   Exp[-(# - 7)^2/2]} &, 3, Darker[Red, 0.3],   3],
  h[{1, #} &,                  4, Black, 3],    h[{0.65 + #/3, 0.1} &,       4, Black, 2],
  h[{5.65 + #/3, 0.1} &,       4, Black, 2],    h[{#, 1} &,                  4, Black, 3],
  h[{3 + #/6, 7 - 2 #/5} &,    8, Black, 1.25], h[{5, 7.5 + #/4} &,          4, Black, 2.5],
  h[{4.5 + #/2, 9.7 + #/75} &, 4, Black, 3],    h[{9, 7.5 + #/4} &,          3, Black, 2.25],
  h[{4.5 + #/2, 7.7} &,        1, Black, 2.25], h[{3 + #/6, 7 - 2 #/5} &,    8, Black, 1.25],
  h[{4.85, 0.5 + 2 #/25} &,    8, Black, 1.25],
 Graphics[{
   Text[Style["What's wrong with \n this challenge?",FontFamily->"Humor Sans", 14],{7,8.75}],
   Text[Style["This is a nice curve isn't it ?",     FontFamily->"Humor Sans", 14],{4,7   }],
   Text[Style["Peak",                                FontFamily->"Humor Sans", 14],{5.,0.1}],
   Arrow[{{1, 7},      {1, 9}}],         Arrow[{{7, 1},      {9, 1}}],
   Arrow[{{8.5, 0.1},  {9, 0.1}}],       Arrow[{{1.75, 0.1}, {1., 0.1}}],
   Arrow[{{4.5, 3.5},  {4.6, 3.2}}]}]},
 AspectRatio -> 2.5/3, PlotRange -> All]

to get this:

xkcd-style plot with "Humor Sans" caption

Then the sky is the limit ;-)

EDIT

The code of Mr.Wizard below is in fact more powerful. As an Illustration,

  Show[{{AbsoluteThickness[2], Circle[{-0.2, 0.2}, 1],
  Line[{{0, -1}, {1/2, -4}}],
  Line[{{1/2, -4}, {-1/2, -8}}],
  Line[{{1/2, -4}, {3/2, -8}}],
  Line[{{0, -1}, {1, -2}}],
  Line[{{1, -2}, {3, -2}}],
  Line[{{0, -1}, {3, -3/2}}],
  Line[{{0.2, 1.5}, {0.2, 3}}],
  Line[{{0.2, 5}, {0.2, 7}}],
  Text[Style["It's time to automate\n comic Strip production", 16], {-0.7, 8}],
  Text[Style["It's so easy\n to do in mathematica !", 16], {-0.7, 4}]} // Graphics,
  ParametricPlot[{Sin[x], Cos[x]}, {x, 0, 2 Pi}, MaxRecursion -> 0, 
  PlotPoints -> 30, Axes -> False, PlotStyle -> Black]
  } ]// xkcdify

produces this xkcd-style plot with "Humor Sans" caption

EDIT2

Couldn't resist one of my favorites (using Simon Wood's solution this time):

  << BlackBodyRadiation`
  pl = BlackBodyProfile[4000 Kelvin, 5000 Kelvin, 6000 Kelvin, 
  PlotRange -> {{0, 2.0*10^-6}, {0, 1.1*10^14}}, 
  Epilog -> {Text[
  Style["\nSCIENCE: \nit works bitches !", 64], {15 10^-7, 
   5 10^13}],Text[I[f] == (2*f^3*h)/(c^2*(-1 + E^((f*h)/(k*T)))), {15 10^-7, 
   0.8 10^14}]
  }] // xkcdConvert

Mathematica graphics


Time to join in the fun. version 2

Result

xkcd-style plot

Method

I produce the basic plot with ticks and labels:

Plot[{x/2, (x + Sin[x])/2.2}, {x, 0, 2 Pi}, MaxRecursion -> 0, 
 PlotPoints -> 30, Axes -> False, Frame -> {True, True, False, False},
 FrameTicks -> {{{0.2, "Start", 0.07}, {3, "lunch", 0.05}, {6, "Finish", 0.06}}, None},
 PlotLabel -> Style["the race", 20],
 Epilog -> {Text["Hare", {1.7, 2}], Text["Tortoise", {4, 0.6}]}
]

I add a couple of lines from the labels to the plot lines with the 2D Drawing Tools "Line segments" tool, then xkcdify:

plot before xkcdify

I make sure that vertical lines also receive a proper wiggle as shown here:

Plot[{3 Sin@x, Cos@x, Tan[x]}, {x, 0, 2 Pi},
  MaxRecursion -> 0, PlotPoints -> 30, PlotRange -> {-2, 2},
  Axes -> False, Frame -> {True, True, False, False},
  FrameTicks -> {
    {{1, "ThrEe", 0.07},
     {3.5, "LitTle", 0.04},
     {6, "Pigs", 0.06}}, None}
] // xkcdify

xkcd-style trig plots

Code

(* Thanks to belisarius & J. M. for refactoring *)

split[{a_, b_}] :=
  If[a == b, {b}, With[{n = Ceiling[3 Norm[a - b]]}, Array[{n - #, #}/n &, n].{a, b}]]

partition[{x_, y__}] := Partition[{x, x, y}, 2, 1]

nudge[L : {a_, b_}, d_] := Mean@L + d Cross[a - b];

gap = {style__, x_BSplineCurve} :>
        {{White, AbsoluteThickness[10], x}, style, AbsoluteThickness[2], x};

wiggle[pts : {{_, _} ..}, d_: {-0.15, 0.15}] :=
  ## &[# ~nudge~ RandomReal@d, #[[2]]] & /@ partition[Join @@ split /@ partition@pts]

xkcdify[plot_Graphics] :=
  Show[FullGraphics@plot, TextStyle -> {17, FontFamily -> "Humor Sans"}] /.
    Line[pts_] :> {AbsoluteThickness[2], BSplineCurve@wiggle@pts} //
  MapAt[# /. gap &, #, {1, 1}] &