Random ink blotches from tikz

How can I create this type of effect in tikz, random ink spots from a fountain pen, for a watermark on select feature pages.

See update.

\documentclass{article}
\usepackage{tikz}
\begin{document}
\usetikzlibrary{calc}

\def\blob#1#2{\draw[fill,rounded corners=#1*3mm] (#2) +($(0:#1*2+#1*rnd)$)
\foreach \a in {20,40,...,350} {  -- +($(\a: #1*2+#1*rnd)$) } -- cycle;}

\begin{tikzpicture}
\blob{0.4}{0,0}
\foreach \a in {0,20,...,350} {
\fill[black] let \p1 = (\a+20*rnd:3*rnd),
\n1 = {0.2*rnd}
in (\p1) circle(\n1);
}

\blob{0.2}{1,3}
\foreach \a in {0,20,...,350} {
\fill[black] let \p1 = ($(1,3)+(\a+20*rnd:2*rnd)$),
\n1 = {0.15*rnd}
in (\p1) circle(\n1);
}
\end{tikzpicture}
\end{document}


Explanations

The picture is composed by two kind of "objects". The irregular shaped blotches, which are drawn by the macro \blob, and lots of smaller black circles.

The \blob macro takes two arguments. The first one is a scale factor. The second one is the coordinates of its center. The macro uses polar coordinates to generate a series of points at regular angles, but random distances from its center, and connects all of them with a rounded corners polyline, which is filled in black.

The smaller circles are drawn with a similar technique, using polar coordinates but this time not only the distance to the center, but also the angles are random. The center of each drop is calculated as the point \p1. The radius of each drop is also random, and precomputed in \n1.

For some unknown reason, if I tried to avoid the let...in syntax and use directly rnd in the expressions, such as circle(0.2*rnd), for example, I got some non-circular drops (very eccentric ellipses), so I resorted to the let...in syntax to precompute the center and radii, and the problem disappeared.

Update

As requested in a comment, I added shadows and shines. It was more difficult than expected, because, since all the shapes and locations are inherently random, but the shadows and shines had to be drawn at the same places and with the same shape than the random drops, I had to store the points of the contour to draw it twice. Also, since some drops can be drawn one "on top" of the other (because of its random positioning), this could cause the shadow of a drop to be drawn on top of the ink of another drop, which is ugly. I had to add pgflayers to ensure that all shadows are in the background.

I tried to devise a better algorithm to decide the size of the random drops, basend on their distance to the center of the splat. I'm not entirely satisfied with the result, but it is not too bad.

Code

\documentclass{article}
\usepackage{tikz}
\begin{document}
\usetikzlibrary{calc}

% Initial setup
\pgfmathsetseed{1234}
\pgfdeclarelayer{ink}
\pgfdeclarelayer{lights}

% Macro to draw the big random shaped  blobs
% Compute the points of the path
\foreach \a in {0,20,...,350} {
\path let
\p1 = (#1),             % center
\n1 = {#2+(#3-#2)*rnd}  % distance at center
in (\p1) node[coordinate] (P\a) at +(\a:\n1) {};
}
% Draw first the shadow (-.5mm, -1mm) is the offset
\fill let \n1 = {(#3-#2)*10/6} in
[fill=black!20,,rounded corners=\n1 mm] ($(P0)+(-.5mm,-1mm)$)
\foreach \a in {20,40,...,350} {  -- ($(P\a)+(-.5mm,-1mm)$) } -- cycle;
\end{pgfonlayer}
% Then the ink
\begin{pgfonlayer}{ink}
\draw let \n1 = {(#3-#2)*10/6} in
[fill,rounded corners=\n1 mm] (P0)
\foreach \a in {20,40,...,350} {  -- (P\a) } -- cycle;
\end{pgfonlayer}
% I tried to draw a light near to the upper border, using the same path, but
% the results were bad, and I deleted it
}

% Macro to draw a drop of ink, relative to one splat
\pgfmathsetmacro{\distance}{#3+(#4-#3)*rnd}
\pgfmathsetmacro{\distancetoborder}{abs(\distance-#3)}
\pgfmathsetmacro{\size}{#3/(15+10*rnd)/sqrt(\distancetoborder)}
\pgfmathsetmacro{\angle}{#2+20*rnd}
\fill[black!20] ($(#1)+(-\size/5,-\size/3)$) +(\angle:\distance) circle(\size);
\end{pgfonlayer}
\begin{pgfonlayer}{ink}
\fill[black]    (#1) +(\angle:\distance) circle(\size);
\end{pgfonlayer}
\begin{pgfonlayer}{lights}
\fill[white]    (#1) ++(\angle:\distance) ++(80:0.7*\size) circle(0.2*\size);
\end{pgfonlayer}
}

% Main drawing
\begin{tikzpicture}
\blob{0,0}{1}{3}
\foreach \a in {0,10,...,350} {
\drop{0,0}{\a}{2.5}{5}
}
\blob{2,4}{0.3}{1}
\foreach \a in {0,20,...,350} {
\drop{2,4}{\a}{1}{3}
}
\end{tikzpicture}
\end{document}


Result

• The problem with the rnd circles becoming ellipses is due to the fact that circle internally sets both x radius and y radius to the same value, rnd in this case, which gets evaluated separately and therefore leads to two different radii. Same problem as in tex.stackexchange.com/a/119209/2552 – Jake Jun 27 '13 at 16:46
• Could replicate the shading from the OP image? That would be very nice. – dustin Jun 28 '13 at 0:36
• +1: nice!! I think you can also try to use radial shadings to get better effects: perhaps How to create a ball shading and to customize 3D lighting manually? can help. – Claudio Fiandrino Jun 28 '13 at 12:03

With PSTricks. Too long as a comment.

\documentclass[pstricks,border=12pt]{standalone}
\usepackage{pst-plot,pst-node}
\usepackage[nomessages]{fp}
\pstVerb{realtime srand}
\expandafter\FPseed\expandafter=\pdfuniformdeviate 1000000

\def\blotches#1{%
\psLoop{#1}{%
\def\points{}%
\rput(!Rand 12 mul 3 sub Rand 12 mul 3 sub){%
\FPrandom\NP
\FPeval\NP{round(NP*17+3:0)}%
\curvepnodes[plotpoints=\NP]{0}{360}{Rand t PtoC}{P}%
\multido{\i=0+1}{\Pnodecount}{\xdef\points{\points(P\i)}}%
\expandafter\psccurve\expandafter*\points}}%
\ignorespaces}

\def\bubbles#1{%
\psLoop{#1}{%
\qdisk(!Rand 12 mul 3 sub Rand 12 mul 3 sub){!Rand 4 div}}%
\ignorespaces}

\begin{document}
\begin{pspicture}(-3,-3)(3,3)
\bubbles{100}
\blotches{20}
\end{pspicture}
\end{document}


Animation

To ease choosing the most realistic output.

Ink

\documentclass[pstricks,border=12pt]{standalone}
\usepackage{pst-plot,pst-node}
\usepackage[nomessages]{fp}
\pstVerb{realtime srand}
\expandafter\FPseed\expandafter=\pdfuniformdeviate 1000000

\def\blotches#1{%
\psLoop{#1}{%
\def\points{}%
\rput(!Rand 12 mul 3 sub Rand 12 mul 3 sub){%
\FPrandom\NP
\FPeval\NP{round(NP*13+7:0)}%
\curvepnodes[plotpoints=\NP]{0}{360}{Rand 2 mul t PtoC}{P}%
\multido{\i=0+1}{\Pnodecount}{\xdef\points{\points(P\i)}}%
\expandafter\psccurve\expandafter*\points}}%
\ignorespaces}

\def\bubbles#1{%
\psLoop{#1}{%
\qdisk(!Rand 12 mul 3 sub Rand 12 mul 3 sub){!Rand 4 div}}%
\ignorespaces}

\begin{document}
\psLoop{10}{%
\begin{pspicture}(-3,-3)(3,3)
\bubbles{200}
\blotches{10}
\end{pspicture}}
\end{document}


Blood

Hopefully blood blotches are more interesting.

\documentclass[pstricks,border=12pt]{standalone}
\usepackage{pst-plot,pst-node}
\usepackage[nomessages]{fp}
\pstVerb{realtime srand}
\expandafter\FPseed\expandafter=\pdfuniformdeviate 1000000

\def\blotches#1{%
\psLoop{#1}{%
\def\points{}%
\rput(!Rand 12 mul 3 sub Rand 12 mul 3 sub){%
\FPrandom\NP
\FPeval\NP{round(NP*13+7:0)}%
\curvepnodes[plotpoints=\NP]{0}{360}{Rand 1.5 mul t PtoC}{P}%
\multido{\i=0+1}{\Pnodecount}{\xdef\points{\points(P\i)}}%
\expandafter\psccurve\expandafter*\points}}%
\ignorespaces}

\def\bubbles#1{%
\psLoop{#1}{%
\qdisk(!Rand 12 mul 3 sub Rand 12 mul 3 sub){!Rand 4 div}}%
\ignorespaces}

\psset{linecolor=red}
\begin{document}
\psLoop{10}{%
\begin{pspicture}(-3,-3)(3,3)
\bubbles{250}
\blotches{10}
\end{pspicture}}
\end{document}


Attention

Note that Rand no longer produces a random real number between 0 and 0.5 inclusive. Its definition had been tacitly changed. Now it produces a random real number between 0 and 1 inclusive. It is not documented, nor announced, but it is still fun!

The code given above has not been updated yet so it will produce different output. I have no time to update it right now. Sorry for this inconvenience.