I'm trying to generate the following, which took about 60 seconds in my favourite vector-drawing too, but I'd like some advice on how to render the same thing using tikz:

enter image description here

I've cobbled together a MWE (below) using tikz-picture, which 'works' (more or less', but feels like a 'kludge'. Basically, I use a 'fake second row' in a matrix of nodes (to let me draw the curved path leaving from ~middle of the path connecting N1 and N2), and a 'fake' empty node in the middle of the path connecting N1 and N2. The issue I've struggled with is -- how to draw a path from the middle of another path? I've convinced myself (perhaps in error), that you can only run a path from one node to another node. Hence, the use of 'fake, empty' nodes to get there from here.

Is there a more elegant way to handle this sort of thing? For some of my work, this kind of diagram shows up a lot, and it would be nice to work from a more general, robust approach than a bespoke kludge for each case.

Here is the MWE:






 \textbf{phantom node \& phantom second row trick}

   \begin{tikzpicture}[>=stealth,->,shorten >=1.5pt,transform canvas={scale=1.05},line 
   \matrix (a) [matrix of nodes,row sep=0.5em, column sep=8em, every node/.style= 
{circle,draw,font=\normalsize\sffamily,minimum width=1.25cm,anchor=center},
 empty.node/.style={draw=none,align=left,minimum width=1.5cm},
 fake.node/.style={draw=none,align=left,minimum width=0.0cm}]
 { |[empty.node]| & [-2em]|[n.node]|$N_1$  & [-5em]|[fake.node]| & |[n.node]|$N_2$ & [-2em]| 
[empty.node]| \\
 |[empty.node]| & [-2em] |[empty.node]| & [-5em]|[fake.node]| & |[empty.node]| & [-2em]| 
[empty.node]| \\};]
 \path[>=stealth,font=\small] (a-1-2) edge node[above,near end] {$\epsilon g(N_1,N_2)$} (a-1-4);
 \path[>=stealth,font=\small] (a-1-2) edge node[below,near start] {$g(N_1,N_2)$} (a-1-4);
 \path[>=stealth,->,font=\small] (a-1-1) edge node [above] {$f(N_1)$} (a-1-2) ;
 \path[>=stealth,->,font=\small] (a-1-4) edge node [above] {$h(N_2)$} (a-1-5) ;
 \path[>=stealth,->,font=\small] (a-1-3) edge[out=0,in=100, bend left=22]  (a-2-4) ;


3 Answers 3


You can set a coordinate on the segment between nodes and give it a name for later use. Default position is halfway, but you can adjust that with pos= the same as for nodes.

enter image description here




\node[draw, circle](n1) at (0,0) {$N_1$};
\node[draw, circle](n2) at (4,0) {$N_2$};
\draw[-stealth] (n1)--coordinate[pos=.4](m)
    node[scale=.6, below, pos=.2]{$g(N_1,N_2)$}
    node[scale=.6, above, pos=.8]{$\varepsilon g(N_1,N_2)$}
\draw[-stealth] (m) to[out=0, in=135] ++(1,-.5);
\draw[-stealth](n2)--node[scale=.6, above]{$h(N_2)$}++(1.5,0);
\draw[stealth-](n1)--node[scale=.6, above]{$f(N_1)$}++(-1.5,0);


  • Great -- a very nice and useful extension to the first suggested 'solution'. I tend to think of everything I draw as involving nodes and paths in a matrix, but I'm now thinking might be time to consider a different approach for some sorts of problems. Commented Feb 1, 2023 at 20:11

A solution using elementary tikz.

enter image description here

  \node[circle,draw,right=4 of n1](n2){$N_2$};
     (n1) -- node[above]{\scriptsize$f(N_1)$} +(-2,0);
     (n1) -- node[below,pos=0.25]{\scriptsize$g(N_1,N_2)$}
             node[above,pos=0.75]{\scriptsize$\varepsilon g(N_1,N_2)$} (n2);
     (n2) -- node[above]{\scriptsize$h(N_2)$} +(2,0);
     (n1)+(1.5,0) .. controls +(1.5,0) and +(-0.4,0.3) .. +(3.5,-0.5);
  • Great -- and clearly, much simpler. I've tried to avoid Bezier curve-like approaches to things, but using controls definitely made the solution easier - in terms of code - provided one understands the positions. Commented Feb 1, 2023 at 20:09

Combining TikZ-CD and a pic.

Using sloped and allow upside down the drive off arrow is always tangent to the line you place it on. Its optional argument can be used to change the appearence of the arrow (color, line width). With drive off = flip you can easily change the direction, other transformations are valid, too, of course.

In this case, we could also just draw a normal arrow just coming from the coordinate in the middle but the drive off pic allows us to place it along any line without having to worry about the correct angles as shown with an extra arrow.

The bending library is loaded so that the curved line is not changed because of the arrow tip. The calculation of positions along the path is using the original curve before the arrow tip was added.

The difference can be seen by redrawing the same path without an arrow tip:

\ar[ll, bend right, ultra thick, postaction={-, draw, dashed, help lines}]

which results in enter image description here

Just loading the bending library will activate the flex option which already results in the line remaining how it should be:

enter image description here

Without the bending library, the quick option is active and can be re-installed by


in case you really need the speed for other curves. For nodes the difference is usually not noticeable.


\usetikzlibrary{cd, bending}
  pics/drive off/.style={
    /tikz/sloped, /tikz/allow upside down,
    code={\draw (0pt,0pt) .. controls +(0:.5cm) and +(135:.5cm) .. (1cm,-.5cm);}}}
  drive off/.style={/tikz/every to/.append style={
    edge node={pic[behind path,#1]{drive off}}}},
  my diagram/.style = {
    /tikz/> = Stealth, arrows = ->, cells = {shape = circle, nodes = draw},
    /tikz/c/.style = {shape = coordinate, yshift = axis_height}}}
\begin{tikzcd}[column sep=1.75cm, my diagram]
  |[c]| \rar["f(N_1)"]
& N_1   \rar["{g(N_1,N_2)}"', dash]
& |[c]| \rar["{\varepsilon g(N_1,N_2)}", drive off=at start]
& N_2   \rar["h(N_2)"] 
        \ar[ll, bend right, drive off = red]
& |[c]|


enter image description here

  • This is pretty slick. I can replicate, but am wondering why the start of the 'drive off' and the arc from which it originates (arc connection N2 -> N1), is not 'smoothly integrated'? Meaning , if you zoom in, the 'drive off' doesn't look like it emanates smoothly from the arc -- you can see the start of the 'drive off' separately from the arc. Something to play with. Commented May 23, 2023 at 18:59
  • @JohnnyCanuck This is due to the arrow tip changing the curve slightly which is not transparent to the mechanic calculating the position along the path. Just loading the bending library which activates the flex option should solve this. I've updated my answer accordingly. Commented May 23, 2023 at 20:00
  • I've also removed a comment about the pic inheriting all the settings from its parent path. This is not correct, at least not in this case (might be because of how TikZ-CD implement its \ar key). The color will not get inherited and changing the line width on the parent path will not change the line width of the pic but the size of the arrow. Weird! Commented May 23, 2023 at 20:09
  • Thanks. First time working with tikzcd -- how does one change the line width for everything in the diagram? Adding /tikz/line width=1.0pt to drive off style affects those arcs, but...;what about everything else? The default thickness (0.4mm?) is way to thin for my purposes. If this should be a separate question, let me know. Commented May 24, 2023 at 13:38
  • @JohnnyCanuck The default line width is 0.4pt (thin). Normally, just doing \tikzset{thick} for example in the preamble would be finde but TikZ-CD sets a custom line width that is derived from some line width of math mode (think \to) which means you will need to do something like \tikzcdset{diagrams=thick, arrows=thick}. Or, if you want it only to affect my diagrams you add thick, arrows=thick to that style. Commented May 24, 2023 at 13:49

You must log in to answer this question.

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