24

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.

3
  • 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, 2016 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, 2016 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, 2016 at 21:35

3 Answers 3

27
+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

4
  • 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? Oct 17, 2016 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) Oct 17, 2016 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, 2016 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, 2016 at 0:04
19

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}

17

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

1
  • 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, 2016 at 10:49

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .