In my search of a good electromagnetic spectrum using LaTeX I came up short and thus I needed to create my own image. Here's how I did it.

  • Well I thought I'd be helpful and answer my own question (how to create a electromagnetic spectrum in latex). Since the "ask question" even have a "answer your own question" option I thought it would be okay? Please forgive my ignorance. – Jimmy Westberg Jan 13 '17 at 9:23
  • 2
    Unrelated (or somewhat related) but maybe useful: pgf-spectra offers an easy way to draw chemical elements spectrum. For other Underground TikZ libraries/packages see this link. – Guilherme Zanotelli Jan 13 '17 at 12:23
\usepackage{siunitx} % SI-units
\usepgfplotslibrary{units} % to add units easily to axis
\usepgfplotslibrary{fillbetween} % to fill inbetween curves
\usepgfplotslibrary{colormaps} % to create colormaps
\pgfplotsset{width=12.2cm, height=7cm}
\pgfplotsset{compat=newest} %(making it only compatalbe with
%new releases of pgfplots)

\begin{tikzpicture}[fill between/on layer={axis grid}]
xticklabel style = {font=\tiny,yshift=0.2ex},
x unit=\si{\micro\meter},
legend cell align=left,
legend style={at={(0.85,-0.77)},anchor=north}
\addplot[draw=none, name path=start, forget plot] coordinates{(10^-5,0)(10^-5,1)};
\addplot[draw=none, name path=gamma, forget plot] coordinates{(10^-3,0)(10^-3,1)};
\addplot[draw=none, name path=xrays, forget plot] coordinates{(10^-2,0)(10^-2,1)};
\addplot[draw=none, name path=uv, forget plot] coordinates{(0.4,0)(0.4,1)};
\addplot[draw=none, name path=visible, forget plot] coordinates{(0.7,0)(0.7,1)};
\addplot[draw=none, name path=ir, forget plot] coordinates{(10^2.5,0)(10^2.5,1)};
\addplot[draw=none, name path=microwave, forget plot] coordinates{(10^5,0)(10^5,1)};
\addplot[draw=none, name path=radiowave, forget plot] coordinates{(10^9,0)(10^9,1)};
\addplot[violet!20, area legend] fill between[of=start and gamma];
\addplot[violet!60, area legend] fill between[of=gamma and xrays];
\addplot[violet, area legend] fill between[of=xrays and uv];
\addlegendentry{Ultra violet}
\addplot[shading=visiblelight, area legend] fill between[of=uv and visible];
\addlegendentry{Visible light}
\addplot[red, area legend] fill between[of=visible and ir];
\addplot[Bittersweet, area legend] fill between[of=ir and microwave];
\addlegendentry{Micro wave}
\addplot[Brown, area legend] fill between[of=microwave and radiowave];
\addlegendentry{Radio wave}

Example output:


| improve this answer | |

Based on the existing answer, I also created an approach. It has some issues that could need work, especially for the 'Visible Spectrum' Scale and names of the colors at the bottom. It also basically falls apart for any other wavelength exponents other than the ones specified in the code already.


\usetikzlibrary{calc, positioning, shapes, backgrounds, fit, arrows}




\pgfdeclarehorizontalshading{visiblelight}{50bp}{% https://tex.stackexchange.com/a/348492/120853


    \pgfmathtruncatemacro{\nextminexponent}{\minexponent + 1}

    % Main foreach loop, drawing the wavelengths as powers of 10 in an alternating fashion: even on top, odd at bottom. Then connects them with help lines
    \foreach [count=\i, remember=\exponent as \previousexponent, evaluate=\i as \currentposition using int(\i/2)] \exponent in {\minexponent, \nextminexponent, ..., \maxexponent}{

        % Anchor at baseline to get all nodes on same baseline.
        % https://tex.stackexchange.com/questions/133227/how-to-align-text-in-tikz-nodes-by-baseline#comment300863_133227
        \node[anchor=base] (WAVELENGTH_\exponent) at (\exponent, \height) {\contour{white}{\num{e\exponent}}};

        \ifnum\i > 1
                \node (LABEL_\currentposition)
                    at ($(WAVELENGTH_\exponent)!0.5!(WAVELENGTH_\previousexponent)$)
                    {};% This is left as a node as opposed to coordinate: fill it out with '\currentposition' for debugging
                % Do not draw connection at exponent 1:
                \pgfmathparse{\exponent != 1}% \pgfmathparse stores result (0 or 1) in macro \pgfmathresult
                \ifnum\pgfmathresult = 1
                    \draw[help lines]
                        (WAVELENGTH_\previousexponent) --(WAVELENGTH_\exponent)
                        node[midway] (CONNECTION_\currentposition) {}% This is left as a node as opposed to coordinate: fill it out with '\currentposition' for debugging
                        coordinate[at start] (CONNECTION_\currentposition_START)
                        coordinate[at end] (CONNECTION_\currentposition_END);

    % Create an arrow shape that fits around all relevant nodes, but do not draw it.
    % Draw it manually later to leave out the 'bottom' of the arrow.
    % We still need this invisible arrow for lining up of coordinates
        single arrow,
        single arrow head extend=0pt,
        single arrow tip angle=150,% Inner angle of arrow tip
        inner sep=0pt
    (ARROW) {};

    \node[align=center] (THERM) at ([yshift=3em]WAVELENGTH_1|-ARROW.after tail) {thermal\\radiation};% Only works because exponent 1 is between -1 and 3
    \draw (THERM) -| ([yshift=-1.5em]WAVELENGTH_-1|-THERM);
    \draw (THERM) -| ([yshift=-1.5em]WAVELENGTH_3|-THERM);

    % On background layer so already drawn arrow and scale lines cover it up nicely
    \begin{scope}[on background layer]
            inner sep=0pt,
            outer sep=0pt,
            fit={([xshift=-2.2em]WAVELENGTH_0|-ARROW.after tail)([xshift=-2.2em]WAVELENGTH_1|-ARROW.before tail)}, shading=visiblelight]
            (SMALL_VISIBLE_LIGHT) {};

            left color=white,
            right color=violet!25,
            middle color=violet!5,
            outer sep=0pt
            (CONNECTION_3_START) -- (CONNECTION_3_END) -- ([xshift=\pgflinewidth]SMALL_VISIBLE_LIGHT.south west) -- ([xshift=\pgflinewidth]SMALL_VISIBLE_LIGHT.north west) -- cycle;

            left color=red!25,
            right color=white,
            middle color=red!5,
            outer sep=0pt,
            (CONNECTION_5_START) -- (CONNECTION_5_END) -- ([xshift=-\pgflinewidth]SMALL_VISIBLE_LIGHT.south east) -- ([xshift=-\pgflinewidth]SMALL_VISIBLE_LIGHT.north east) -- cycle;

    % Some labels can be drawn automatically at the designated label coordinates:
    \foreach [count=\i] \label in {
            {},%Skip this one
            \node[raylabel, align=center] at (LABEL_\i) {\label};

    % These do not fit the loop and are drawn manually:
    \node[raylabel, align=right, anchor=north] at ([yshift=-1em]$(WAVELENGTH_-2)!0.45!(WAVELENGTH_0)$) {Ultra-\\violet};

    \node[raylabel, fill=white] at (CONNECTION_6) {radio waves};

    \node[raylabel, left=0.1em of CONNECTION_1, align=right] {cosmic\\rays};

        below=4em of SMALL_VISIBLE_LIGHT,
        label=above:{\textbf{Visible Spectrum}}
        ] (FULL_VISIBLE_LIGHT) {%
        \pgfspectra[width=13em,height=3em]\\%pgfspectra also has a builtin axis which of course much better than this terrible approach, but it is in nanometer
            {\num{0.38} \hfill\num{0.48} \hfill\num{0.58}\hfill \num{0.68} \hfill\num{0.78}}\\
        {\hfill blue \hspace{0.1em} green yellow \hfill red \hfill}

    % Draw 'magnifying' trapeze, on background so it is covered by scale labels
    \begin{scope}[on background layer]
        \filldraw[help lines, fill=black!10] (FULL_VISIBLE_LIGHT.north east) -- (SMALL_VISIBLE_LIGHT.south east) -- (SMALL_VISIBLE_LIGHT.south west) -- (FULL_VISIBLE_LIGHT.north west) -- cycle;

    % Draw around arrow manually, leaving its tail open
    \draw[draw, thick] (ARROW.after tail) -- (ARROW.before head) -- (ARROW.before tip) -- (ARROW.tip) -- (ARROW.after tip) -- (ARROW.after head) -- (ARROW.before tail);

giving an electromagnetic spectrum with some annotations, generated semi-automatically:

electromagnetic spectrum with annotations

| improve this answer | |

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.