> ## Documentation Index
> Fetch the complete documentation index at: https://wiki.latch.bio/llms.txt
> Use this file to discover all available pages before exploring further.

# Develop your First Plot Notebook

Latch Plots are similar to Jupyter Notebooks, built around Python cells that you can chain together to analyze, visualize, and interact with your data.

Unlike standard notebooks, Latch Plots allow you to:

* Define interactive input widgets using pure Python
* Enable reactive execution, where input changes automtically trigger dependent cells
* Instantly convert notebooks into shareable, interactive Apps

In the walkthrough below, you'll learn how to build an interactive App using Latch Plots in under 10 minutes.

<Steps>
  <Step title="Start a New Notebook">
    * Navigate to the Latch Plots tab at [https://console.latch.bio/plots](https://console.latch.bio/plots).
    * Click **Start from a blank layout**.
  </Step>

  <Step title="Add a Python Cell">
    * Click “+ Analysis” to add your first Python cell.
    * The cell will be pre-populated with code that defines an input widget for loading a CSV file from either Latch Data or Latch Registry.
    * To proceed:
      * Select the Latch Data option to open the file browser.
      * Navigate to: welcome > gsea\_pathway > CoCl2 vs Control (Condition).csv
      * Click the file to load it into your Plot.

    <video autoPlay muted loop playsInline className="w-full aspect-video" class="rounded-md">
      <source src="https://mintcdn.com/latchbio/rZf7ybfryGn4thLo/images/plots/basics/choose-file.mp4?fit=max&auto=format&n=rZf7ybfryGn4thLo&q=85&s=a30abd564a174d641973561684ec6de0" data-path="images/plots/basics/choose-file.mp4" />
    </video>

    <Tip>
      Code explanation:
      The `.value` attribute in `datasource.value` does two things:

      * Returns the selected value from the widget (in this case, a Pandas DataFrame from the selected file or table).
      * Subscribes the current cell to the widget, so that if the user changes their selection, the cell will automatically re-run with the new value.

      This is part of what makes Latch Plots reactive: your code updates in real time as users interact with the interface.
    </Tip>
  </Step>

  <Step title="Add another cell to plot the data">
    Let's create a volcano plot to visualize differential gene expression.\
    You'll also add two text input widgets so users can adjust the significance thresholds (e.g., log₂ fold change and p-value cutoff) interactively to highlight differentially expressed genes.

    <Accordion title="Copy paste this code to your notebook">
      You can put any content in here, including other components, like code:

      ```python theme={null}
        import plotly.express as px
        import numpy as np
        from lplots.widgets.text import w_text_input
        from lplots.widgets.plot import w_plot
        from lplots.widgets.row import w_row

        # Create input widgets for thresholds
        padj_threshold = w_text_input(
            label="Adjusted p-value threshold (-log10)",
            default="1.3"  # -log10(0.05)
        )

        lfc_threshold = w_text_input(
            label="Log2 Fold Change threshold",
            default="1"
        )

        # Create the volcano plot
        def create_volcano_plot(df, padj_thresh, lfc_thresh):
            # Convert threshold strings to floats
            padj_thresh = float(padj_thresh)
            lfc_thresh = float(lfc_thresh)

            # Add a column to color points based on significance and fold change
            df = df.copy()
            df['-log10(padj)'] = -np.log10(df['padj'])

            conditions = [
                (df['-log10(padj)'] > padj_thresh) & (df['log2FoldChange'].abs() > lfc_thresh),
                (df['-log10(padj)'] > padj_thresh) & (df['log2FoldChange'].abs() <= lfc_thresh),
                (df['-log10(padj)'] <= padj_thresh) & (df['log2FoldChange'].abs() > lfc_thresh)
            ]

            values = [
                'Significant & High Fold Change',
                'Significant Only',
                'High Fold Change Only'
            ]

            df['Significance'] = np.select(conditions, values, default='Not Significant')

            # Create the volcano plot
            fig = px.scatter(
                df,
                x='log2FoldChange',
                y='-log10(padj)',
                color='Significance',
                color_discrete_map={
                    'Significant & High Fold Change': 'red',
                    'Significant Only': 'blue',
                    'High Fold Change Only': 'green',
                    'Not Significant': 'grey'
                },
                opacity=0.6,
                template="simple_white"
            )

            # Add threshold lines
            fig.add_hline(y=padj_thresh, line_dash="dash", line_color="grey")
            fig.add_vline(x=lfc_thresh, line_dash="dash", line_color="grey")
            fig.add_vline(x=-lfc_thresh, line_dash="dash", line_color="grey")

            # Update layout
            fig.update_layout(
                title="Volcano Plot of Differential Expression",
                xaxis_title="Log2 Fold Change",
                yaxis_title="-log10(Adjusted p-value)",
                legend_title="Significance"
            )

            return fig

        # Create plot
        fig_volcano = create_volcano_plot(df, padj_threshold.value, lfc_threshold.value)

        # Display widgets in a row and plot
        w_column(items=[
            w_row(items=[padj_threshold, lfc_threshold]),
            w_plot(source=fig_volcano)
        ])
      ```
    </Accordion>

    Click Run to execute the cell. Then, try adjusting the input values. Your volcano plot will automatically update in response.

    <video autoPlay muted loop playsInline className="w-full aspect-video" class="rounded-md">
      <source src="https://mintcdn.com/latchbio/rZf7ybfryGn4thLo/images/plots/basics/plot.mp4?fit=max&auto=format&n=rZf7ybfryGn4thLo&q=85&s=0ff014c8a49aff5d2d9784cceea0e5ee" data-path="images/plots/basics/plot.mp4" />
    </video>
  </Step>

  <Step title="Convert your notebook into an app">
    On the left-hand side of the notebook, next to the "Run All" button, click the "Switch to App Mode" icon.

    <video autoPlay muted loop playsInline className="w-full aspect-video" class="rounded-md">
      <source src="https://mintcdn.com/latchbio/rZf7ybfryGn4thLo/images/plots/basics/app.mp4?fit=max&auto=format&n=rZf7ybfryGn4thLo&q=85&s=71cedb827470c9cd5cc618b79aeb1b0e" data-path="images/plots/basics/app.mp4" />
    </video>

    In App Mode, the following elements from Edit Mode are hidden:

    * Python code cells
    * Cell borders
    * Run buttons
    * Developer-specific sidebar features (e.g., versioning)

    This gives end users a clean, intuitive, and fully interactive App experience.
  </Step>

  <Step title="Turn your App into a reproducible Plot Template">
    Now that you've set up the interface and logic for your App, it's time to create a **Plot Template.**

    Templates package everything—your code, widgets, visualizations, and environment—into a reusable blueprint. This makes it easy for scientists to launch their own dedicated Plot Notebooks preloaded with your App, and start analyzing data instantly.

    To create a Template:

    * On the right handside of your Plot notebook, click "Save Version". Provide your version with a descriptive name.
    * Click the three dots next to the newly created version. Select "Create new template from version".
    * Provide a name and description for your template.

    <video autoPlay muted loop playsInline className="w-full aspect-video" class="rounded-md">
      <source src="https://mintcdn.com/latchbio/rZf7ybfryGn4thLo/images/plots/basics/version.mp4?fit=max&auto=format&n=rZf7ybfryGn4thLo&q=85&s=b3f4b5386ef32e488390e966071e2552" data-path="images/plots/basics/version.mp4" />
    </video>
  </Step>
</Steps>

That's it! You have successfully created your first App and saved it as a reproducible template for the entire team.
