22

How can one simulate chalkboard lines in Tikz?

Chalk texture on blackboard. Arrows, circles and other shapes.

Perhaps something in this lines could work, but a) would it then be necessary to specify lines as regions? and b) I cannot compile the code in the previous link, which makes pdflatex -shell-escape wondering:

[. . .]
Package pgfplots notification 'compat/show suggested version=true': document ha
s been generated with the most recent feature set (\pgfplotsset{compat=1.12}).

Runaway definition?
->
! TeX capacity exceeded, sorry [main memory size=5000000].
\pgfplotsapplistXXpushback@smallbufoverfl ...toka
                                                  \the \t@pgfplots@tokb \the...
l.16   \addplot3[surf,shader=interp] {rand};

!  ==> Fatal error occurred, no output PDF file produced!
Transcript written on papyrus.log.

Edit: It seems that chalk-like filling can be produced using gaussian noise. I have no idea of how to do implement that with tikz/pgf though.

  • The answer clearly states For this fine of a texture, LuaLaTeX must be used (dynamic memory allocation). For samples=100 or fewer, any modern engine can be used. Did you reduce the number of samples sufficiently to use pdfLaTeX i.e. to no more than 100? – cfr Oct 17 '16 at 1:04
  • PGF/TikZ is not the best way to do this kind of thing - not even for filling areas, but especially for lines. It does clean, precise and technical well. It does random, messy and organic poorly. Not to say you can't do it. But you can't do it easily or efficiently the way you might with a more suitable tool. <Grumbles a bit about people insisting on using screwdrivers to bang nails rather than perfectly good hammers.> – cfr Oct 17 '16 at 1:28
  • @cfr I agree. That said, pgf is really useful, as a notation and as machinery. Do you know of a programmable interface to "random, messy and organic" graphics programs? – Tássio Oct 17 '16 at 21:35
24
+500

It takes ages.

\documentclass[tikz,border=5]{standalone}
\usetikzlibrary{decorations,backgrounds}
\pgfkeys{decoration/.cd,
  iterations/.store in=\pgfdecorationiterations, iterations=75,
  radius/.store in=\pgfdecorationradius, radius=0.25pt,
}
\pgfdeclaredecoration{chalk}{draw}{
\state{draw}[width=\pgfdecorationsegmentlength]{
\pgftransformxshift{0.5\pgfdecorationsegmentlength/2}
\pgfmathloop
\ifnum\pgfmathcounter>\pgfdecorationiterations
\else
\pgfpathcircle{\pgfpointpolar{rnd*360}{rnd*\pgfdecorationsegmentamplitude}}%
{\pgfdecorationradius}%
\repeatpgfmathloop
}}
\begin{document}
\begin{tikzpicture}[background rectangle/.style={fill=black},
  show background rectangle, chalk/.style={fill=white, decorate, 
  decoration={chalk, segment length=1.5pt, amplitude=3pt}
  },looseness=0.25]
\path [chalk] (1/8,2) -- (0,1/2) arc (180:315:1/2) (-1/2,3/2)
  to [bend right] (5/8,3/2);
\path [chalk, shift=(0:1)] (1/8,1) to [bend left] (0,0);
\path [chalk, shift=(0:3/2)] (1/8,2) to [bend left] (0,0)
   (2/3,1) -- (1/16,2/3) -- (2/3,0);
\path [chalk, shift=(0:5/2)] (0,1) to [bend left] (1,1)
   to [bend left] (0,0) to [bend left] (1,0);
\path [chalk] (-1,-3/4) to [bend left] (9/2,-1/2);
\end{tikzpicture}
\end{document}

enter image description here

  • Looks great, but I'm getting Tex Capacity Exceeded [main memory size = ... when compiling ... Had to increase radius while reducing iterations. Anything in options needing to be changed to get compile with your number of iterations? – Guido Jorg Oct 17 '16 at 8:47
  • 2
    @GuidoJorg I compiled with lualatex (depending on your TeX distribution you may have to add \RequirePackage{luatex85} in the line before the \documentclass) – Mark Wibrow Oct 17 '16 at 8:56
  • This is great! I wonder if there is a way to compress the result, since the file you get is very large. – Tássio Oct 17 '16 at 10:51
  • 2
    This is, as always, fantastic. I still say that TikZ is not the best tool for this job, though ;). – cfr Oct 18 '16 at 0:04
16

The following code looks like @Mark Wibrow's answer but takes only 20 seconds to compile. The idea is that instead of drawing millions of points, we draw thousands of dotted lines with various width, phase, pattern, and shift, which corresponding to \a, \b, \c, and \d in my code.

\documentclass[border=9,tikz]{standalone}
\usetikzlibrary{backgrounds,decorations.pathreplacing}
\begin{document}

\def\iterate{400}
\def\rollabcd{
    \pgfmathsetmacro\a{(1+rnd)/4}
    \pgfmathsetmacro\b{5+5*rnd}
    \pgfmathsetmacro\c{\b*rnd}
    \pgfmathsetmacro\d{rnd*3}
}
\tikzset{
    draw abcd/.style={
        white,line cap=round,
        line width=\a,
        dash pattern=on 0 off \b,
        dash phase=\c,
        shift={(rnd*360:\d pt)}
    },
    chalk/.style={
        decorate,
        decoration={
            show path construction,
            lineto code={
                \foreach\i in{1,...,\iterate}{
                    \rollabcd
                    \draw[draw abcd](\tikzinputsegmentfirst)--(\tikzinputsegmentlast);
                }
            },
            curveto code={
                \foreach\i in{1,...,\iterate}{
                    \rollabcd
                    \draw[draw abcd](\tikzinputsegmentfirst)..controls(\tikzinputsegmentsupporta)
                                          and(\tikzinputsegmentsupportb)..(\tikzinputsegmentlast);
                }
            },
            closepath code={
                \foreach\i in{1,...,\iterate}{
                \rollabcd
                    \draw[draw abcd](\tikzinputsegmentfirst) -- (\tikzinputsegmentlast);
                }
            }
        }
    }
}

\tikz[background rectangle/.style={fill=black},show background rectangle,looseness=0.25]{
\path [chalk] (1/8,2) -- (0,1/2) arc (180:315:1/2) (-1/2,3/2)
  to [bend right] (5/8,3/2);
\path [chalk, shift=(0:1)] (1/8,1) to [bend left] (0,0);
\path [chalk, shift=(0:3/2)] (1/8,2) to [bend left] (0,0)
   (2/3,1) -- (1/16,2/3) -- (2/3,0);
\path [chalk, shift=(0:5/2)] (0,1) to [bend left] (1,1)
   to [bend left] (0,0) to [bend left] (1,0);
\path [chalk] (-1,-3/4) to [bend left] (9/2,-1/2);
}

\end{document}

Possible Optimization

The alphabet is taken from Custom line cap to simulate inked line in TikZ.
The current approach replace each Bezier curve by 32 dotted curves.
For each dotted curve, roll dice to determine

  • the dash pattern and dash phase
  • shift
  • line cap (either round or rect)
  • color (either black or white)

It takes 10 seconds to compile

\documentclass[border=9,tikz]{standalone}
\usetikzlibrary{backgrounds,decorations.pathreplacing}
\begin{document}

\def\niterate{32}
\def\rolldice{
    \pgfmathsetmacro\rndlinewidth{32/(8+\i)}
    \pgfmathsetmacro\rndoff{4+8*rnd}
    \pgfmathsetmacro\rndshift{((4-\rndlinewidth)*256*rnd)^.25}
    \pgfmathrandomitem\rndcap{cap}
    \pgfmathsetmacro\rnddark{rnd<.3?100:0}
}
\pgfmathdeclarerandomlist{cap}{{round}{rect}}
\tikzset{
    put dots/.style={
        /utils/exec=\rolldice,
        line width=\rndlinewidth,
        dash pattern=on 0 off \rndoff,
        dash phase=(1+\rndoff)*rnd,
        shift={(rnd*360:\rndshift pt)},
        line cap=\rndcap,
        black!\rnddark,
    },
    chalk/.style={
        decorate,
        decoration={
            show path construction,
            lineto code={
                \foreach\i in{1,...,\niterate}{
                    \draw[put dots]
                        (\tikzinputsegmentfirst)--(\tikzinputsegmentlast);
                }
            },
            curveto code={
                \foreach\i in{1,...,\niterate}{
                    \draw[put dots]
                        (\tikzinputsegmentfirst)..controls
                    (\tikzinputsegmentsupporta)and(\tikzinputsegmentsupportb)
                        ..(\tikzinputsegmentlast);
                }
            },
            closepath code={
                \foreach\i in{1,...,\niterate}{
                \draw[put dots]
                    (\tikzinputsegmentfirst)--(\tikzinputsegmentlast);
                }
            }
        }
    },
}
\tikzset{
    A/.pic={\draw[chalk] 
    (0,-0.55) -- (0.3,0.4) -- (0.6,-0.55);
    \draw[chalk](0.1,1/3-0.45) --
    (0.5,1/3-0.45);
    \path (0.7,0);},
    B/.pic={\draw[chalk] (0,-0.45) -- (0,0.45)
    to[out=0,in=0,looseness=2.5]    (0,0)   to[out=0,in=0,looseness=3] cycle;},
    C/.pic={\draw[chalk]
     (0,0) to[out=90,in=110,looseness=2]    (0.5,0.25);
     \draw[chalk](0,0) to[out=-90,in=-110,looseness=2]  (0.5,-0.25);
     \path (0.7,0);},
    D/.pic={\draw[chalk] (0,-0.45) -- (0,0.45) 
    to[out=0,in=0,looseness=2.25]    cycle;
    \path (0.7,0);},
    E/.pic={\draw[chalk] 
    (0.5,-0.45) --(0,-0.45) -- (0,0.45) -- (0.5,0.45);
    \draw[chalk] (0,0) -- (0.5,0);
    \path (0.7,0);},
    F/.pic={\draw[chalk] 
    (0,-0.45) -- (0,0.45)   -- (0.5,0.45);
    \draw[chalk] (0,0) -- (0.5,0);
    \path (0.7,0);},
    G/.pic={\draw[chalk]
     (0,0) to[out=90,in=110,looseness=2]    (0.5,0.25);
     \draw[chalk] (0,0) to[out=-90,in=-110,looseness=2] 
     (0.5,-0.25);
     \draw[chalk] (0.54,-0.25) to (0.3,-0.25);
     \path (0.7,0);},
    H/.pic={\draw[chalk] 
    (0,-0.5) -- (0,0.5);    
    \draw[chalk] (0.5,-0.5) -- (0.5,0.5);
    \draw[chalk] (0,0) -- (0.5,0);
    \path (0.7,0);},
    I/.pic={\draw[chalk] (0,-0.45) -- (0,0.45);
    \path (0.25,0);},
    J/.pic={\draw[chalk] (0.2,0.45) -- (0.2,-0.35) to[out=-90,in=0]
    (0.1,-0.45) to[out=180,in=-90] (0,-0.35);
    \path (0.45,0);},
    K/.pic={\draw[chalk] 
    (0,-0.45) -- (0,0.45); 
    \draw[chalk] (0.4,0.45) -- (0.02,0) --  (0.4,-0.45);
    \path (0.6,0);},
    L/.pic={\draw[chalk] 
    (0,0.5) -- (0,-0.45) -- (0.4,-0.45);
    \path (0.6,0);},
    M/.pic={\draw[chalk] (0,-0.45) -- (0,0.45) -- 
    (0.3,0.25) -- (0.6,0.45) -- (0.6,-0.45);
    \path (0.8,0);},
    N/.pic={\draw[chalk] (0,-0.45) -- (0,0.45) -- (0.6,-0.4) --
    (0.6,0.45);
    \path (0.8,0);},
    O/.pic={\draw[chalk] (0.3,0) circle(0.3 and 0.48);
    \path (0.8,0);},
    P/.pic={\draw[chalk] (0,-0.45) -- (0,0.45) 
    to[out=0,in=0,looseness=2.5]    (0,0);
    \path (0.6,0);},
    Q/.pic={\draw[chalk] 
    (0.3,0) circle(0.3 and 0.48);
    \draw[chalk](0.35,-0.25) -- (0.6,-0.45);
    \path (0.8,0);},
    R/.pic={\draw[chalk] 
    (0,-0.45) -- (0,0.45) 
    to[out=0,in=0,looseness=2.5]    (0.05,0) -- (0.4,-0.45);
    \path (0.6,0);},
    S/.pic={\draw[chalk] (0.5,0.4)  
    to[out=160,in=165,looseness=2]  (0.3,0) 
    to[out=-15,in=-20,looseness=2] (0.1,-0.4);
    \path (0.65,0);},
    T/.pic={\draw[chalk] (0.35,-0.45) -- (0.35,0.45) (0,0.45) -- (0.7,0.45);
    \path (0.85,0);},
    U/.pic={\draw[chalk] (0,0.5) -- (0,0) to[out=-90,in=-90,looseness=2.5]
    (0.6,0) -- (0.6,0.5);
    \path (0.8,0);},
    V/.pic={\draw[chalk] (0,0.5) -- (0.3,-0.4) -- (0.6,0.5);
    \path (0.8,0);},
    W/.pic={\draw[chalk] (0,0.45) -- (0.3,-0.4) -- (0.45,-0.1)
    -- (0.6,-0.4) -- (0.9,0.45);
    \path (1.1,0);},
    X/.pic={\draw[chalk] 
    (0,0.45) -- (0.6,-0.45);
     \draw[chalk] (0.6,0.45)
    -- (0,-0.45);
    \path (0.8,0);},
    Y/.pic={\draw[chalk] 
    (0,0.45) -- (0.3,0);    
    \draw[chalk] (0.6,0.45)
    -- (0,-0.45);
    \path (0.8,0);},
    Z/.pic={\draw[chalk] (0,0.45) --(0.6,0.45) -- (0,-0.45) 
    -- (0.6,-0.45);
    \path (0.8,0);},
    space/.pic={\path (0,0) (0.2,0);},
}
\tikz[every pic/.style={scale=4},scale=3]{
    \fill(0,-2)rectangle(9,8);
    \begin{scope}[overlay]
        \path foreach\X[count=\x]in {A,...,G}{(\x,6)pic{\X}};
        \path foreach\X[count=\x]in {H,...,N}{(\x,4)pic{\X}};
        \path foreach\X[count=\x]in {O,...,U}{(\x,2)pic{\X}};
        \path foreach\X[count=\x]in {V,...,Z}{(\x,0)pic{\X}};
    \end{scope}
}


\message{^^J^^J time = \the\numexpr\pdfelapsedtime*1000/65536 ms ^^J^^J}

\end{document}

15

This isn't quite what the OP asks, but it is along those lines. It uses the handwritten Teen Spirit Font (fts), superimposed with a random dot pattern, using JLDiaz's answer at How to have a real random pattern?, using the \specw[<speckle count per letter>]{word}

\documentclass{article}
\usepackage{tikz,stackengine}
\pagecolor{black}
\color{white}
\newcommand\speckle[2][50]{%
  \setbox0=\hbox{\color{white}#2}%
  \stackinset{c}{}{c}{}{%
  \color{black}%
    \begin{tikzpicture}
      \foreach \i in {1,...,#1}
      \fill (rnd*\wd0, rnd*\ht0) circle (.25pt);
    \end{tikzpicture}%
  }%
  {\copy0}%
}
\newcommand\specw[2][75]{%
  \specwaux{#1}#2\relax\relax\relax%
}
\def\specwaux#1#2#3\relax{%
  \ifx\relax#2\else\speckle[#1]{#2}\specwaux{#1}#3\relax\fi%
}
\begin{document}
\centering
\fontfamily{fts}\selectfont
\specw[30]{Today\,s} \specw[90]{Lesson:}

\specw[300]{{\scalebox{3}{\char116}}}
\specw[700]{{\scalebox{3}{\char64}}}
\specw[1000]{{\scalebox{3}{U}}}
\end{document}

enter image description here

At the expense of compilation time, one can decrease the size of the dots (from .25pt to .10pt) and double the number of dots:

enter image description here

For comparison, without speckles, it looks like this:

enter image description here

  • This is very useful! Especially to get text matching the style of the images. Thanks! Didn't mark as solution because I need diagrams to have this effect. – Tássio Oct 17 '16 at 10:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.