Is there a library (or something similar) available for optics including e.g. convex and concave lenses? I fount this but it seems that this is not available as an official library …


6 Answers 6


There is an extensive library in pstricks - maybe it helps. http://www.ctan.org/tex-archive/graphics/pstricks/contrib/pst-optic

  • 3
    Thanks Boris but I prefer using TikZ further on …
    – Tobi
    Nov 2, 2011 at 17:16

AFAIK, such a library does not exist yet.

As you mentioned, I started something a while ago, but I did not have, at that time, enough tikz/pgf knowledge to circumvent some difficulties I found.

The idea is to use the shapes/nodes mechanism to define anchors (such as image focus, center, etc.) and then use them to draw useful light rays.

Here is an example of the syntax I would have liked to have (hope it is clear enough).

  \node[converging lens,anchor=center,image focal length=2.5cm] (L1) at (0,0)
  \node[diverging lens,anchor=center,image focal length=-1.5cm] (L2)
    at ($(L1.center)!5cm!(L1.optical axis forward)$) {$\mathcal{L}_2$};

  \node[object for lens=L1,height=1.5cm,anchor=bottom] (Object1) at
      length}!(L1.optical axis forward)$) {};
  \draw[LR>] (Object1.top) -- ($(L1.top)!(Object1.top)!(L1.bottom)$)
    -- (Object1.image top by L1);
  \draw[LR>>] (Object1.top) -- (L1.center) -- (Object1.image top by L1);
  \draw[LR>>>] (Object1.top) -- 
    ($(L1.top)!(Object1.image top by L1)!(L1.bottom)$) -- 
    (Object1.image top by L1);

  \node[object for lens=L2,height={TO BE COMPUTED},anchor=bottom] (Object2) at
    (Object1.image top by L1) {};

One of the not-so-easy part is to have anchor names depending on node names (such as in the above example Object1.image top by L1). It should also be possible to know if an image is real or virtual and draw the light rays (LR) consistently.

Unfortunately, lack of time led me to a more rustic solution. Here I give a (too?) verbose example of what can be done with tikz (not a library but a kind of template):





\pgfdeclareshape{mark point +}{%
  \inheritsavedanchors[from=mark point |]

% Light rays

  LRnoarrow/.style = {thick,gray,nearly opaque},
  LR/.style 2 args = {%
    decoration={markings,mark=at position #2 with {\arrow{#1};}},
  VirtualLR/.style = {LRnoarrow,dashed},
  LR>/.style = {LR={>}{#1}},
  LR>/.default = {0.5},
  LR>>/.style = {LR={>>}{#1}},
  LR>>/.default = {0.55},
  LR>>>/.style = {LR={>>>}{#1}},
  LR>>>/.default = {0.6},
  LR>>>>/.style = {LR={>>>>}{#1}},
  LR>>>>/.default = {0.65},
  ConvergingLens/.style = {ultra thick,<->},
  DivergingLens/.style = {ultra thick,>-<},
  OpticalAxis/.style = {very thick,->},
  Object/.style = {very thick,->},
  VirtualObject/.style = {very thick,->,dashed}}

% Annotate an angle
\pgfkeysdef{/tikz/mark angle/start angle}{\tikzset{start angle=#1}}
\pgfkeysdef{/tikz/mark angle/end angle}{\tikzset{end angle=#1}}
\pgfkeysdef{/tikz/mark angle/angle radius}{\tikzset{radius=#1}}
\pgfkeyssetvalue{/tikz/mark angle/label radius}{1cm}
\pgfkeyssetvalue{/tikz/mark angle/label pos}{.5}
\pgfkeyssetvalue{/tikz/mark angle/node options}{}
\pgfkeyssetvalue{/tikz/mark angle/path options}{}
  % #1 optional parameters
  % #2 coordinate of the center
  % #3 coordinate giving the start direction
  % #4 coordinate giving the end direction
  % #5 label
    \coordinate (xCJtikz@AngleCenter) at (#2);
    \coordinate (xCJtikz@AngleStart) at (#3);
    \coordinate (xCJtikz@AngleEnd) at (#4);
    \ifdim\AngleEnd pt<\AngleStart pt\relax
      /tikz/mark angle/.cd,
      angle radius=1cm,
      label radius=1.2cm,
      label pos=.5,
      start angle=\AngleStart,
      end angle=\AngleEnd,
        \noexpand\draw[\pgfkeysvalueof{/tikz/mark angle/path options}]
          radius}!(xCJtikz@AngleStart)\noexpand$) arc;
        \noexpand\node[\pgfkeysvalueof{/tikz/mark angle/node options}] at
          angle/label pos}*\AngleEnd-\pgfkeysvalueof{/tikz/mark
          angle/label pos}*\AngleStart:\pgfkeysvalueof{/tikz/mark angle/label radius})\noexpand$)}%


  \coordinate (OpticalAxisLeft) at (0,0);
  \coordinate (OpticalAxisRight) at ($(OpticalAxisLeft)+(13,0)$);
  \draw[OpticalAxis] (OpticalAxisLeft) -- (OpticalAxisRight);
  % Lens 1
  \coordinate[label=below left:$O_1$] (Center1) at
  \coordinate[label=above:$\mathcal{L}_1$] (Top1) at
  \coordinate (Bottom1) at ($(Center1)!-1!(Top1)$);
  \draw[ConvergingLens] (Bottom1) -- (Top1);
  (ObjectFocus1) at
  ($(Center1)!-\FocalLengthOne!(OpticalAxisRight)$) {}; 
  \node[mark point +,label=above left:$F'_1$] (ImageFocus1) at
  ($(Center1)!\FocalLengthOne!(OpticalAxisRight)$) {}; 
  \coordinate (ImageFocalPlane1Top) at
  \coordinate (ImageFocalPlane1Bottom) at
  % Lens 2
  \coordinate[label=below left:$O_2$] (Center2) at
  \coordinate[label=above:$\mathcal{L}_2$] (Top2) at
  \coordinate (Bottom2) at ($(Center2)!-1!(Top2)$);
  \draw[DivergingLens] (Bottom2) -- (Top2);
  \node[mark point +,label=below:$F_2'$] (ImageFocus2) at 
  ($(Center2)!\FocalLengthTwo!(OpticalAxisRight)$) {}; 
  \coordinate[label=below left:$F_2$] (ObjectFocus2) at
  ($(Center2)!-\FocalLengthTwo!(OpticalAxisRight)$) {}; 
  \coordinate (ImageFocalPlane2Top) at
  \coordinate (ImageFocalPlane2Bottom) at
  % Object at infinity
  \coordinate (LRBegin) at
  \coordinate (temp) at ($(LRBegin)+(-\ObjectAngle:1)$);
  \coordinate (IncidencePoint) at (intersection cs: first line =
  {(Top1) -- (Bottom1)}, second line = {(LRBegin) -- (temp)});
  \coordinate (LRThroughCenter1Begin) at
  \coordinate (Image1Top) at (intersection cs: first line =
  {(LRThroughCenter1Begin) -- (Center1)}, second line =
  {(ImageFocalPlane1Top) -- (ImageFocalPlane1Bottom)});
  \draw[red,dotted,thick] (ImageFocalPlane1Top) --
  \draw[red,dotted,thick] (LRThroughCenter1Begin) --
  \node[coordinate,label=below right:$B_1$] at (Image1Top) {};
  \node[coordinate,label=below right:$A_1$] at (ImageFocus1) {};
  \draw[Object,semitransparent] (ImageFocus1) -- (Image1Top);
  \coordinate (LRIntersectionWithLens2) at
  (intersection cs: first line = {(Top2) -- (Bottom2)}, second line
  = {(IncidencePoint) -- (Image1Top)});
  \coordinate (LRThroughCenter2Begin) at
  \coordinate[label=above right:$I$]
  (LRThroughCenter2IntersectionWithImageFocalPlane2) at 
  (intersection cs: first line = {(ImageFocalPlane2Top) --
    (ImageFocalPlane2Bottom)}, second line =
  {(LRThroughCenter2Begin) -- (Center2)});
  \coordinate (Image1Bottom) at
  \coordinate (Image1TopOnLens2) at
  \draw[LR>] (LRBegin) -- node[above,sloped]
  {$\leftarrow B_{\infty}$} (IncidencePoint);
  \draw[LR>] (IncidencePoint) -- (LRIntersectionWithLens2);
  \draw[dotted] (LRIntersectionWithLens2) -- (Image1Top);
  \draw[blue,dotted,thick] (ImageFocalPlane2Top) --
  -- (Center2); 
  (LRIntersectionWithLens2)$) -- node[above,sloped,very near start]
  {$\leftarrow B'_{\infty}$} (LRIntersectionWithLens2); 
  \draw[LR>] (LRIntersectionWithLens2) --
  \coordinate (PictureBottomLeft) at (OpticalAxisLeft |-
  \coordinate (PictureTopRight) at (OpticalAxisRight |- Top2);
  \draw[ultra thin,semitransparent] ($(PictureBottomLeft)+(0,-.5)$)
  grid[step=1mm] ($(PictureTopRight)+(0,.5)$);
  \draw[very thin,semitransparent] ($(PictureBottomLeft)+(0,-.5)$)
  grid[step=5mm] ($(PictureTopRight)+(0,.5)$);
  \draw[thin,semitransparent] ($(PictureBottomLeft)+(0,-.5)$)
  grid[step=1cm] ($(PictureTopRight)+(0,.5)$);
  \coordinate (Image1TopOnLens1) at ($(Top1)!(Image1Top)!(Bottom1)$);
  \draw[LR>>] ($(Image1TopOnLens1)!1cm!(ObjectFocus1)$) --
  \draw[LR>>] (Image1TopOnLens1) -- (Image1TopOnLens2);
  \draw[LR>>] (Image1TopOnLens2) -- ($(Image1TopOnLens2)!-.3!(ImageFocus2)$);
  \draw[VirtualLR] (Image1TopOnLens2) --
  \tikzMarkAngle[path options={<-,DarkOrange}]%
  \tikzMarkAngle[path options={<-,DarkOrange}]%
  \tikzMarkAngle[path options={<-,DarkOrange}]%



  • You can get key-dependent anchor names using deferred anchors. I recently came across this and find it pretty interesting. I'll try to have a look soon and see if I can come up with a start to tackling this kind of issue. Nov 2, 2011 at 18:59
  • 2
    @wh1t3 I know, I implemented it! ;-)
    – cjorssen
    Nov 2, 2011 at 19:14
  • Thanks for this idea, but I’m looking rather for a library to print the shapes of objects like lenses etc. and not the rays between them. I guess I’ll have to make my own small library …
    – Tobi
    Nov 3, 2011 at 17:40

This is my solution using TikZ with the calc and intersection libraries.

\documentclass{standalone}% or wathever you want

% load packages
\usepackage{tikz, xcolor}
% load libraries

% define light and dark gray

% make some settings
    % style for the intersecting path, which
    % are nessesary for the calculation but
    % shouldn't be drawn in the final image
%       draw,% comment this aout after construction
    % style for an arrow used as object
    optical arrow/.style={%
        inner sep=3pt,
        shape=single arrow,
        minimum width=0.5cm,
        minimum height=1.5cm,
        outer sep=0pt,
        shape border rotate=90,
    % style for the virtual image
    virtual optical arrow/.style={%
        inner sep=3pt,
        shape=single arrow,
        minimum width=0.5cm,
        minimum height=1.5cm,
        outer sep=0pt,
        shape border rotate=90,
    % style for the mirror
        line width=2pt,
    % style for the axis
    optical axis/.style={%
    % style for light rays
    % style for imagined rays, which ar not real
    % but help by constructin the image
    imagined ray/.style={%
        ray, dgray, -,
    % alias
    virtual ray/.style={imagined ray},
    % style for (focal) points
        inner sep=1pt,
        minimum size=2pt,
        outer sep=2pt

% set three layers
% and define shortcuts to access them

    % define the bounding box is nessesarx because the ipaths
    % make it bigger than needed
    \path [use as bounding box] (-5.2,-5) rectangle (6.2,5);
    % define variables, you may vary them a little
    %% radius
    %% focal distancs = \radius/2
    %% object size
    %% object width
    % draw mirror
    %% the extra ipath is nessesary to get nicer rays
    \path [ipath, name path=M] (\radius,0) ++(90:\radius)
          arc (90:270:\radius);
        \draw [mirror] (\radiusII-0.05,0) ++(130:\radiusII)
              arc (130:240:\radiusII);
    % draw focal point
    \node (B) at (\focal,0) [point] {};
    % draw object
    \node (O) [optical arrow,anchor=tail, minimum height=\size] %
          at (\owidth,0) {};
    %% description
    \node [above right] at (O.tip) {object};
    % rays
    %% draw axis ray
    \draw [ray] (O.tip) -- (0,0) -- ($(0,0)!3!(\owidth,-\size)$);
    %% draw parallel ray
    \path [ipath, name path=PS] (O.tip) -- ++(-3,0);
    \draw [ray, name intersections={of=M and PS, by=M-PS}]
        (O.tip) -- (M-PS) -- ($(M-PS)!2!(B)$);
    %% caculate virtual axis ray
    \path [ipath, name path=AS-V] ($(0,0)!-4!(\owidth,-\size)$) -- (0,0);
    %% calculate virtual parallel ray
    \path [ipath, name path=PS-V] ($(M-PS)!-4!(B)$) -- (M-PS);
    %% draw virtual axis ray
    \draw [imagined ray, name intersections={of=AS-V and PS-V, by=Tip-V}]
        (Tip-V) -- (0,0);
    %% draw virtual axis ray
    \draw [imagined ray] (Tip-V) -- (M-PS);
    % draw virtual object
    \bglayer{\path let \p{1}=(Tip-V) in 
        (Tip-V) node (V) [minimum height=\size,
                          virtual optical arrow,anchor=tip
                         ] {};}
    %% description
    \path (V.west) node [left] {virtual image};
    % draw optical axis
    \fglayer{\draw [optical axis] (-5,0) --++(11,0);}

width drawn ipaths

final image


A couple of days ago a new package called tikz-optics, written by Michel Fruchart, was added to CTAN, providing a library called optics.

The documentation is thus far only available in French as far as I can see, but there are multiple examples that demonstrates what the library can do.

See also GitHub: https://github.com/fruchart/tikz-optics


Here is an attempt to use the node/shape mecanism to automate the positionning of images of objects by optical system. It is, at the moment, just a proof of concept. Feel free to comment.

enter image description here



  optics/.is family,
  % Thin centered optical system
  optical system/.is family,
  optical system/.cd,
  image focal length/.initial = 1.5cm,
  object focal length/.initial = -1.5cm,
  upper height/.initial = 1.25cm,
  lower height/.initial = -1.25cm,

  % Object
  object/.is family,
  is object for/.initial = a,
  height/.initial = 1cm,

\pgfdeclareshape{thin centered optical system}{%
        /tikz/optics/optical system/upper height}}}
        /tikz/optics/optical system/lower height}}}
        /tikz/optics/optical system/image focal length}}}
        /tikz/optics/optical system/object focal length}}}
  % Center
  % Top
  % Bottom
  % Principal image focus
  \anchor{principal image focus}{\principalimagefocus}
  % Principal object focus
  \anchor{principal object focus}{\principalobjectfocus}

\pgfdeclareshape{converging lens}{%
        /tikz/optics/optical system/upper height}}}
        /tikz/optics/optical system/lower height}}}
        /tikz/optics/optical system/image focal length}}}
  \inheritsavedanchors[from=thin centered optical system]
  \inheritanchor[from=thin centered optical system]{center}
  \inheritanchor[from=thin centered optical system]{top}
  \inheritanchor[from=thin centered optical system]{bottom}
  \inheritanchor[from=thin centered optical system]{principal image focus}
  \inheritanchor[from=thin centered optical system]{principal object focus}

      \pgfkeysvalueof{/tikz/optics/object/is object for}}}
      \csname pgf@sh@ma@\isobjectfor\endcsname
  % Bottom
  % Top
  % Optical system center
  \anchor{optical system center}{\opticalsystemcenter}
  % Top on optical system
    % This allows to define \savedanchors in terms of other saved anchors.
  \anchor{top on optical system}{\toponopticalsystem}
  % Image top
    % This allows to define \savedanchors in terms of other saved anchors.
      \pgfpointanchor{\isobjectfor}{principal image focus}}{%
  \anchor{image top}{\imagetop}
  % Image top on optical system
    % This allows to define \savedanchors in terms of other saved anchors.
  \anchor{image top on optical system}{\imagetoponopticalsystem}

% Light rays



  > = stealth,
  LRnoarrow/.style = {thick,gray,nearly opaque},
  LR/.style 2 args = {%
    decoration = {markings,mark=at position #2 with {\arrow{#1};}},
    postaction = {decorate},
  VirtualLR/.style = {LRnoarrow,dashed},
  LR>/.style = {LR={>}{#1}},
  LR>/.default = {0.5},
  LR>>/.style = {LR={>>}{#1}},
  LR>>/.default = {0.55},
  LR>>>/.style = {LR={>>>}{#1}},
  LR>>>/.default = {0.6},




\begin{tikzpicture}%[rotate=45,transform shape]
  \node[draw,shape=converging lens,
        optics/optical system/image focal length = 1cm,
        optics/optical system/upper height = 2cm,
        optics/optical system/lower height = -2cm] (L) at (2,0) {};
  \draw[thick,->,-stealth] ($(L)!5cm!90:(L.top)$) coordinate (OpticalAxisLeft) 
        -- ($(L)!5cm!-90:(L.top)$) coordinate (OpticalAxisRight);
        shape = object,
        optics/object/is object for = L] (O) at (-1,0) {};
  \fill[red] (L.principal image focus) circle[radius=2pt];
  \fill[blue] (O.optical system center) circle[radius=2pt];
  \fill[green] (O.top on optical system) circle[radius=2pt];
  \fill[yellow] (O.image top) circle[radius=2pt];
  \draw[LR>] (O.top) -- (O.top on optical system) -- (O.image top);
  \draw[LR>>] (O.top) -- (O.image top);
  \draw[LR>>>] (O.top) -- (O.image top on optical system) -- (O.image top);


I know I'm a little bit late for answering this question, but only recently I had to draw the notable rays of spherical mirror and lens.

After seeing cjorssen's nice answer (here), I decided to create my own package following some ideas presented in this question.

Therefore, below I present some results from my package named tikz-mirror-lens:

The basic concept is using tikzmath to calculate the coordinates (pp, i) of the image given the mirror or lens focus (f), and the coordinates of the object (p, o), and some extra parameters for configuration. The following commands draw the main parts to have the mirror or lens alongside the notable points and rays, and object and image.

The following MWE uses the package and presents the main results. It is also possible to use each constituent command separately and improve the draw freely.






\begin{tikzpicture}[very thick,
        mark= between positions 0.1 and 0.99 step #1pt with {\arrow{latex}}
    extended line/.style={shorten >=-#1,shorten <=-#1},
    extended line/.default=1cm

enter image description here enter image description here

You must log in to answer this question.

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