Week 3 Lab: Opentrons
Our central dogma: Paper protocol -> opentrons protocol -> compiled protocol
My paper protocol:
For this week’s lab, I wanted to make a golden lyre. There are so many steps I won’t write them all out here, but per the tips on the RC Donovan Opentrons art site, I chose to use a 1.0 μL point on a 100mm agar dish with 96 wells.
We are using an Opentrons OT-2.
I used four colors:
mKO2 (gold)
mScarlet_I (reddish pink), for shading
mRFP_1 (deep red)
And mTurquoise_2 (cyan).
I understood at this point that colors will need to be changed, and they were changed in the notebook to align with available colos.
My Opentrons/Compiled protocols: Writing the paper protocol step by step using the terms the Opentrons framework understands (in our case, Python).
To draft my image, I used the RC Donovan automation art interface to draw the golden lyre (as I am passionate about the intersection of science and poetry, as both a poet and a scientist).
In Greek mythology, the lyre is a symbol of divine order and artistic mastery, most famously and closely associated ith the god Apollo and the musician Orpheus. Orpheus is important to poets because he reminds us of art’s power to charm beasts, influence gods, and even to change the course of nature; it represents the supreme power of art, or at the very least, of creativity.
The Golden Lyre of Ur (modern day Iraq) is our world’s oldest string instrument, more than 4500 years old—it pre-dates the construction of the Great Pyramid and Stonehenge.
You can view my draft here:
opentrons-art.rcdonovan.com/?id=9z8hyxg8qp6jjeu
You can view my code here:
https://colab.research.google.com/drive/11BssMhDrq-GOlkEQM7XTrs4J2jQY74Xh#scrollTo=pczDLwsq64mk&line=2&uniqifier=1
And below:
from opentrons import types
metadata = { # see https://docs.opentrons.com/v2/tutorial.html#tutorial-metadata
'author': '',
'protocolName': '',
'description': '',
'source': 'HTGAA 2026 Opentrons Lab',
'apiLevel': '2.20'
}
##############################################################################
### Robot deck setup constants - don't change these
##############################################################################
TIP_RACK_DECK_SLOT = 9
COLORS_DECK_SLOT = 6
AGAR_DECK_SLOT = 5
PIPETTE_STARTING_TIP_WELL = 'A1'
well_colors = {
'A1' : 'Red',
'B1' : 'Green',
'C1' : 'Orange'
}
def run(protocol):
##############################################################################
### Load labware, modules and pipettes
##############################################################################
# Tips
tips_20ul = protocol.load_labware('opentrons_96_tiprack_20ul', TIP_RACK_DECK_SLOT, 'Opentrons 20uL Tips')
# Pipettes
pipette_20ul = protocol.load_instrument("p20_single_gen2", "right", [tips_20ul])
# Modules
temperature_module = protocol.load_module('temperature module gen2', COLORS_DECK_SLOT)
# Temperature Module Plate
temperature_plate = temperature_module.load_labware('opentrons_96_aluminumblock_generic_pcr_strip_200ul',
'Cold Plate')
# Choose where to take the colors from
color_plate = temperature_plate
# Agar Plate
agar_plate = protocol.load_labware('htgaa_agar_plate', AGAR_DECK_SLOT, 'Agar Plate') ## TA MUST CALIBRATE EACH PLATE!
# Get the top-center of the plate, make sure the plate was calibrated before running this
center_location = agar_plate['A1'].top()
pipette_20ul.starting_tip = tips_20ul.well(PIPETTE_STARTING_TIP_WELL)
##############################################################################
### Patterning
##############################################################################
###
### Helper functions for this lab
###
# pass this e.g. 'Red' and get back a Location which can be passed to aspirate()
def location_of_color(color_string):
for well,color in well_colors.items():
if color.lower() == color_string.lower():
return color_plate[well]
raise ValueError(f"No well found with color {color_string}")
# For this lab, instead of calling pipette.dispense(1, loc) use this: dispense_and_detach(pipette, 1, loc)
def dispense_and_detach(pipette, volume, location):
"""
Move laterally 5mm above the plate (to avoid smearing a drop); then drop down to the plate,
dispense, move back up 5mm to detach drop, and stay high to be ready for next lateral move.
5mm because a 4uL drop is 2mm diameter; and a 2deg tilt in the agar pour is >3mm difference across a plate.
"""
assert(isinstance(volume, (int, float)))
above_location = location.move(types.Point(z=location.point.z + 5)) # 5mm above
pipette.move_to(above_location) # Go to 5mm above the dispensing location
pipette.dispense(volume, location) # Go straight downwards and dispense
pipette.move_to(above_location) # Go straight up to detach drop and stay high
###
from opentrons import types
import math
metadata = {
'protocolName': 'HTGAA Golden Lyre',
'author': 'Katharine Kolin',
'source': 'HTGAA 2026',
'apiLevel': '2.20'
}
# --- Deck setup constants ---
TIP_RACK_DECK_SLOT = 9
COLORS_DECK_SLOT = 6
AGAR_DECK_SLOT = 5
PIPETTE_STARTING_TIP_WELL = 'A1'
well_colors = {
'A1' : 'Red', # mrfp1
'B1' : 'Yellow', # mscarlet_i
'C1' : 'Green', # mturquoise2
'D1' : 'Cyan', # mko2
'E1' : 'Hotpink'
}
def run(protocol):
# --- Load labware ---
tips_20ul = protocol.load_labware('opentrons_96_tiprack_20ul', TIP_RACK_DECK_SLOT, 'Opentrons 20uL Tips')
pipette_20ul = protocol.load_instrument("p20_single_gen2", "right", [tips_20ul])
temperature_module = protocol.load_module('temperature module gen2', COLORS_DECK_SLOT)
color_plate = temperature_module.load_labware('opentrons_96_aluminumblock_generic_pcr_strip_200ul', 'Cold Plate')
agar_plate = protocol.load_labware('htgaa_agar_plate', AGAR_DECK_SLOT, 'Agar Plate')
center_location = agar_plate['A1'].top()
pipette_20ul.starting_tip = tips_20ul.well(PIPETTE_STARTING_TIP_WELL)
# --- Helper functions ---
def location_of_color(color_string):
for well, color in well_colors.items():
if color.lower() == color_string.lower():
return color_plate[well]
raise ValueError(f"No well found with color {color_string}")
def dispense_and_detach(pipette, volume, location):
assert(isinstance(volume, (int, float)))
above_location = location.move(types.Point(z=5))
pipette.move_to(above_location)
pipette.dispense(volume, location)
pipette.move_to(above_location)
# --- Your Design Coordinates ---
# Organized by standard color names to match location_of_color()
design_data = {
'Red': [(-9.9, 29.7),(-7.7, 29.7),(-5.5, 29.7),(-3.3, 29.7),(-1.1, 29.7),(1.1, 29.7),(3.3, 29.7),(5.5, 29.7),(7.7, 29.7),
(9.9, 29.7),(-3.3, 27.5),(1.1, 27.5),(5.5, 27.5),(9.9, 25.3),(-16.5, 23.1),(-14.3, 23.1),(-12.1, 23.1),(9.9, 23.1),(12.1, 23.1),
(14.3, 23.1),(16.5, 23.1),(-18.7, 20.9),(-16.5, 20.9),(-14.3, 20.9),(-12.1, 20.9),(-9.9, 20.9),(-7.7, 20.9),(-5.5, 20.9),(-3.3, 20.9),
(-1.1, 20.9),(1.1, 20.9),(3.3, 20.9),(5.5, 20.9),(7.7, 20.9),(9.9, 20.9),(12.1, 20.9),(14.3, 20.9),(16.5, 20.9),(18.7, 20.9),(-12.1, 18.7),
(-9.9, 18.7),(-7.7, 18.7),(-5.5, 18.7),(-3.3, 18.7),(-1.1, 18.7),(1.1, 18.7),(3.3, 18.7),(5.5, 18.7),(7.7, 18.7),(9.9, 18.7),(12.1, 18.7),
(16.5, 18.7),(-12.1, 16.5),(-9.9, 16.5),(-7.7, 16.5),(-5.5, 16.5),(-3.3, 16.5),(-1.1, 16.5),(1.1, 16.5),(3.3, 16.5),(5.5, 16.5),(7.7, 16.5),
(9.9, 16.5),(12.1, 16.5),(-12.1, 14.3),(12.1, 14.3),(-12.1, 12.1),(12.1, 12.1),(-14.3, 9.9),(-12.1, 9.9),(12.1, 9.9),(14.3, 9.9),(-14.3, 7.7),
(-12.1, 7.7),(12.1, 7.7),(14.3, 7.7),(-14.3, 5.5),(14.3, 5.5),(-16.5, 3.3),(-14.3, 3.3),(-12.1, 3.3),(12.1, 3.3),(14.3, 3.3),(16.5, 3.3),
(-18.7, 1.1),(-16.5, 1.1),(16.5, 1.1),(18.7, 1.1),(-18.7, -1.1),(-16.5, -1.1),(16.5, -1.1),(18.7, -1.1),(20.9, -1.1),(-20.9, -3.3),
(-18.7, -3.3),(18.7, -3.3),(20.9, -3.3),(-18.7, -5.5),(18.7, -5.5),(20.9, -5.5),(-18.7, -7.7),(18.7, -7.7),(-18.7, -9.9),(-16.5, -9.9),
(16.5, -9.9),(18.7, -9.9),(-16.5, -12.1),(-14.3, -12.1),(-12.1, -12.1),(-9.9, -12.1),(-7.7, -12.1),(-5.5, -12.1),(-3.3, -12.1),(-1.1, -12.1),
(1.1, -12.1),(3.3, -12.1),(5.5, -12.1),(7.7, -12.1),(9.9, -12.1),(12.1, -12.1),(14.3, -12.1),(16.5, -12.1),(-14.3, -14.3),(-12.1, -14.3),
(-9.9, -14.3),(-7.7, -14.3),(-5.5, -14.3),(-3.3, -14.3),(-1.1, -14.3),(1.1, -14.3),(3.3, -14.3),(5.5, -14.3),(7.7, -14.3),(9.9, -14.3),
(12.1, -14.3),(14.3, -14.3),(16.5, -14.3),(-14.3, -16.5),(-12.1, -16.5),(-9.9, -16.5),(-7.7, -16.5),(-5.5, -16.5),(-3.3, -16.5),(-1.1, -16.5),
(1.1, -16.5),(3.3, -16.5),(5.5, -16.5),(7.7, -16.5),(9.9, -16.5),(12.1, -16.5),(14.3, -16.5),(-3.3, -18.7),(-1.1, -18.7),(1.1, -18.7),
(3.3, -18.7),(-3.3, -20.9),(-1.1, -20.9),(1.1, -20.9),(3.3, -20.9),(-3.3, -23.1),(-1.1, -23.1),(1.1, -23.1),(3.3, -23.1),(-5.5, -25.3),
(-3.3, -25.3),(-1.1, -25.3),(1.1, -25.3),(3.3, -25.3),(5.5, -25.3),(-7.7, -27.5),(-5.5, -27.5),(-3.3, -27.5),(-1.1, -27.5),(1.1, -27.5),
(3.3, -27.5),(5.5, -27.5),(7.7, -27.5),(-9.9, -29.7),(-7.7, -29.7),(-5.5, -29.7),(-3.3, -29.7),(-1.1, -29.7),(1.1, -29.7),(3.3, -29.7),
(5.5, -29.7),(7.7, -29.7),(9.9, -29.7)],
'Hotpink': [(-7.7, 27.5),(9.9, 27.5),(-16.5, 25.3),(-14.3, 25.3),(-12.1, 25.3),(12.1, 25.3),(14.3, 25.3),(16.5, 25.3),(-18.7, 23.1),(18.7, 23.1),
(-18.7, 18.7),(-16.5, 18.7),(18.7, 18.7),(-14.3, 12.1),(14.3, 12.1),(-16.5, 7.7),(16.5, 7.7),(-16.5, 5.5),(-12.1, 5.5),(12.1, 5.5),(16.5, 5.5),
(-18.7, 3.3),(18.7, 3.3),(-20.9, 1.1),(-14.3, 1.1),(14.3, 1.1),(20.9, 1.1),(-20.9, -1.1),(-20.9, -5.5),(-20.9, -7.7),(-16.5, -7.7),(16.5, -7.7),
(20.9, -7.7),(-20.9, -9.9),(20.9, -9.9),(-18.7, -12.1),(18.7, -12.1),(-16.5, -14.3)],
'Cyan': [(-9.9, 27.5),(-5.5, 27.5),(-1.1, 27.5),(3.3, 27.5),(7.7, 27.5),(-9.9, 25.3),(-5.5, 25.3),(-1.1, 25.3),(3.3, 25.3),(7.7, 25.3),(-9.9, 23.1),
(-5.5, 23.1),(-1.1, 23.1),(3.3, 23.1),(7.7, 23.1),(-9.9, 14.3),(-5.5, 14.3),(-1.1, 14.3),(3.3, 14.3),(7.7, 14.3),(-9.9, 12.1),(-5.5, 12.1),(-1.1, 12.1),
(3.3, 12.1),(7.7, 12.1),(-9.9, 9.9),(-5.5, 9.9),(-1.1, 9.9),(3.3, 9.9),(7.7, 9.9),(-9.9, 7.7),(-5.5, 7.7),(-1.1, 7.7),(3.3, 7.7),(7.7, 7.7),(-9.9, 5.5),
(-5.5, 5.5),(-1.1, 5.5),(3.3, 5.5),(7.7, 5.5),(-9.9, 3.3),(-5.5, 3.3),(-1.1, 3.3),(3.3, 3.3),(7.7, 3.3),(-9.9, 1.1),(-5.5, 1.1),(-1.1, 1.1),(3.3, 1.1),
(7.7, 1.1),(12.1, 1.1),(-14.3, -1.1),(-9.9, -1.1),(-5.5, -1.1),(-1.1, -1.1),(3.3, -1.1),(7.7, -1.1),(12.1, -1.1),(-14.3, -3.3),(-9.9, -3.3),(-5.5, -3.3),
(-1.1, -3.3),(3.3, -3.3),(7.7, -3.3),(12.1, -3.3),(16.5, -3.3),(-14.3, -5.5),(-9.9, -5.5),(-5.5, -5.5),(-1.1, -5.5),(3.3, -5.5),(7.7, -5.5),(12.1, -5.5),
(16.5, -5.5),(-14.3, -7.7),(-9.9, -7.7),(-5.5, -7.7),(-1.1, -7.7),(3.3, -7.7),(7.7, -7.7),(12.1, -7.7),(-14.3, -9.9),(-9.9, -9.9),(-5.5, -9.9),
(-1.1, -9.9),(3.3, -9.9),(7.7, -9.9),(12.1, -9.9)],
'Yellow': [(-12.1, 31.9),(-9.9, 31.9),(-7.7, 31.9),(-5.5, 31.9),(-3.3, 31.9),(-1.1, 31.9),(1.1, 31.9),(3.3, 31.9),(5.5, 31.9),(7.7, 31.9),(9.9, 31.9),
(12.1, 31.9),(-12.1, 29.7),(12.1, 29.7),(-18.7, 27.5),(-16.5, 27.5),(-14.3, 27.5),(-12.1, 27.5),(12.1, 27.5),(14.3, 27.5),(16.5, 27.5),(-18.7, 25.3),
(18.7, 25.3),(-20.9, 23.1),(20.9, 23.1),(-20.9, 20.9),(20.9, 20.9),(-20.9, 18.7),(-14.3, 18.7),(14.3, 18.7),(20.9, 18.7),(-20.9, 16.5),(-18.7, 16.5),
(-16.5, 16.5),(-14.3, 16.5),(14.3, 16.5),(16.5, 16.5),(18.7, 16.5),(20.9, 16.5),(-14.3, 14.3),(14.3, 14.3),(-16.5, 12.1),(16.5, 12.1),(-16.5, 9.9),
(16.5, 9.9),(-18.7, 7.7),(18.7, 7.7),(-18.7, 5.5),(18.7, 5.5),(-20.9, 3.3),(20.9, 3.3),(23.1, 3.3),(-23.1, 1.1),(23.1, 1.1),(-23.1, -1.1),(23.1, -1.1),
(-23.1, -3.3),(23.1, -3.3),(-23.1, -5.5),(23.1, -5.5),(-23.1, -7.7),(23.1, -7.7),(-23.1, -9.9),(23.1, -9.9),(-20.9, -12.1),(20.9, -12.1),(-18.7, -14.3),
(18.7, -14.3),(-16.5, -16.5),(16.5, -16.5),(-14.3, -18.7),(-12.1, -18.7),(-9.9, -18.7),(-7.7, -18.7),(-5.5, -18.7),(5.5, -18.7),(7.7, -18.7),(9.9, -18.7),
(12.1, -18.7),(14.3, -18.7),(-5.5, -20.9),(5.5, -20.9),(-5.5, -23.1),(5.5, -23.1),(-7.7, -25.3),(7.7, -25.3),(-9.9, -27.5),(9.9, -27.5),(12.1, -27.5),
(-12.1, -29.7),(12.1, -29.7),(-12.1, -31.9),(-9.9, -31.9),(-7.7, -31.9),(-5.5, -31.9),(-3.3, -31.9),(-1.1, -31.9),(1.1, -31.9),(3.3, -31.9),(5.5, -31.9),
(7.7, -31.9),(9.9, -31.9),(12.1, -31.9)]
}
# --- Patterning Execution ---
for color_name, points in design_data.items():
# Get location for the current color
color_well = location_of_color(color_name)
pipette_20ul.pick_up_tip()
# Batch 15 dots per aspiration to avoid excessive travel
for i in range(0, len(points), 15):
batch = points[i:i+15]
pipette_20ul.aspirate(len(batch), color_well)
for x, y in batch:
# Wall safety check: Radius < 40mm
if math.sqrt(x**2 + y**2) > 40.0:
continue
# Move to the coordinate relative to the plate center
target_point = center_location.move(types.Point(x=x, y=y, z=0))
dispense_and_detach(pipette_20ul, 1, target_point)
pipette_20ul.drop_tip() ###
# Don't forget to end with a drop_tip()
And here is the final image it renders:


Below are my coordinates:
mrfp1_points = [(-9.9, 29.7),(-7.7, 29.7),(-5.5, 29.7),(-3.3, 29.7),(-1.1, 29.7),(1.1, 29.7),(3.3, 29.7),(5.5, 29.7),(7.7, 29.7),(9.9, 29.7),(-3.3, 27.5),(1.1, 27.5),(5.5, 27.5),(9.9, 25.3),(-16.5, 23.1),(-14.3, 23.1),(-12.1, 23.1),(9.9, 23.1),(12.1, 23.1),(14.3, 23.1),(16.5, 23.1),(-18.7, 20.9),(-16.5, 20.9),(-14.3, 20.9),(-12.1, 20.9),(-9.9, 20.9),(-7.7, 20.9),(-5.5, 20.9),(-3.3, 20.9),(-1.1, 20.9),(1.1, 20.9),(3.3, 20.9),(5.5, 20.9),(7.7, 20.9),(9.9, 20.9),(12.1, 20.9),(14.3, 20.9),(16.5, 20.9),(18.7, 20.9),(-12.1, 18.7),(-9.9, 18.7),(-7.7, 18.7),(-5.5, 18.7),(-3.3, 18.7),(-1.1, 18.7),(1.1, 18.7),(3.3, 18.7),(5.5, 18.7),(7.7, 18.7),(9.9, 18.7),(12.1, 18.7),(16.5, 18.7),(-12.1, 16.5),(-9.9, 16.5),(-7.7, 16.5),(-5.5, 16.5),(-3.3, 16.5),(-1.1, 16.5),(1.1, 16.5),(3.3, 16.5),(5.5, 16.5),(7.7, 16.5),(9.9, 16.5),(12.1, 16.5),(-12.1, 14.3),(12.1, 14.3),(-12.1, 12.1),(12.1, 12.1),(-14.3, 9.9),(-12.1, 9.9),(12.1, 9.9),(14.3, 9.9),(-14.3, 7.7),(-12.1, 7.7),(12.1, 7.7),(14.3, 7.7),(-14.3, 5.5),(14.3, 5.5),(-16.5, 3.3),(-14.3, 3.3),(-12.1, 3.3),(12.1, 3.3),(14.3, 3.3),(16.5, 3.3),(-18.7, 1.1),(-16.5, 1.1),(16.5, 1.1),(18.7, 1.1),(-18.7, -1.1),(-16.5, -1.1),(16.5, -1.1),(18.7, -1.1),(20.9, -1.1),(-20.9, -3.3),(-18.7, -3.3),(18.7, -3.3),(20.9, -3.3),(-18.7, -5.5),(18.7, -5.5),(20.9, -5.5),(-18.7, -7.7),(18.7, -7.7),(-18.7, -9.9),(-16.5, -9.9),(16.5, -9.9),(18.7, -9.9),(-16.5, -12.1),(-14.3, -12.1),(-12.1, -12.1),(-9.9, -12.1),(-7.7, -12.1),(-5.5, -12.1),(-3.3, -12.1),(-1.1, -12.1),(1.1, -12.1),(3.3, -12.1),(5.5, -12.1),(7.7, -12.1),(9.9, -12.1),(12.1, -12.1),(14.3, -12.1),(16.5, -12.1),(-14.3, -14.3),(-12.1, -14.3),(-9.9, -14.3),(-7.7, -14.3),(-5.5, -14.3),(-3.3, -14.3),(-1.1, -14.3),(1.1, -14.3),(3.3, -14.3),(5.5, -14.3),(7.7, -14.3),(9.9, -14.3),(12.1, -14.3),(14.3, -14.3),(16.5, -14.3),(-14.3, -16.5),(-12.1, -16.5),(-9.9, -16.5),(-7.7, -16.5),(-5.5, -16.5),(-3.3, -16.5),(-1.1, -16.5),(1.1, -16.5),(3.3, -16.5),(5.5, -16.5),(7.7, -16.5),(9.9, -16.5),(12.1, -16.5),(14.3, -16.5),(-3.3, -18.7),(-1.1, -18.7),(1.1, -18.7),(3.3, -18.7),(-3.3, -20.9),(-1.1, -20.9),(1.1, -20.9),(3.3, -20.9),(-3.3, -23.1),(-1.1, -23.1),(1.1, -23.1),(3.3, -23.1),(-5.5, -25.3),(-3.3, -25.3),(-1.1, -25.3),(1.1, -25.3),(3.3, -25.3),(5.5, -25.3),(-7.7, -27.5),(-5.5, -27.5),(-3.3, -27.5),(-1.1, -27.5),(1.1, -27.5),(3.3, -27.5),(5.5, -27.5),(7.7, -27.5),(-9.9, -29.7),(-7.7, -29.7),(-5.5, -29.7),(-3.3, -29.7),(-1.1, -29.7),(1.1, -29.7),(3.3, -29.7),(5.5, -29.7),(7.7, -29.7),(9.9, -29.7)]
mscarlet_i_points = [(-7.7, 27.5),(9.9, 27.5),(-16.5, 25.3),(-14.3, 25.3),(-12.1, 25.3),(12.1, 25.3),(14.3, 25.3),(16.5, 25.3),(-18.7, 23.1),(18.7, 23.1),(-18.7, 18.7),(-16.5, 18.7),(18.7, 18.7),(-14.3, 12.1),(14.3, 12.1),(-16.5, 7.7),(16.5, 7.7),(-16.5, 5.5),(-12.1, 5.5),(12.1, 5.5),(16.5, 5.5),(-18.7, 3.3),(18.7, 3.3),(-20.9, 1.1),(-14.3, 1.1),(14.3, 1.1),(20.9, 1.1),(-20.9, -1.1),(-20.9, -5.5),(-20.9, -7.7),(-16.5, -7.7),(16.5, -7.7),(20.9, -7.7),(-20.9, -9.9),(20.9, -9.9),(-18.7, -12.1),(18.7, -12.1),(-16.5, -14.3)]
mturquoise2_points = [(-9.9, 27.5),(-5.5, 27.5),(-1.1, 27.5),(3.3, 27.5),(7.7, 27.5),(-9.9, 25.3),(-5.5, 25.3),(-1.1, 25.3),(3.3, 25.3),(7.7, 25.3),(-9.9, 23.1),(-5.5, 23.1),(-1.1, 23.1),(3.3, 23.1),(7.7, 23.1),(-9.9, 14.3),(-5.5, 14.3),(-1.1, 14.3),(3.3, 14.3),(7.7, 14.3),(-9.9, 12.1),(-5.5, 12.1),(-1.1, 12.1),(3.3, 12.1),(7.7, 12.1),(-9.9, 9.9),(-5.5, 9.9),(-1.1, 9.9),(3.3, 9.9),(7.7, 9.9),(-9.9, 7.7),(-5.5, 7.7),(-1.1, 7.7),(3.3, 7.7),(7.7, 7.7),(-9.9, 5.5),(-5.5, 5.5),(-1.1, 5.5),(3.3, 5.5),(7.7, 5.5),(-9.9, 3.3),(-5.5, 3.3),(-1.1, 3.3),(3.3, 3.3),(7.7, 3.3),(-9.9, 1.1),(-5.5, 1.1),(-1.1, 1.1),(3.3, 1.1),(7.7, 1.1),(12.1, 1.1),(-14.3, -1.1),(-9.9, -1.1),(-5.5, -1.1),(-1.1, -1.1),(3.3, -1.1),(7.7, -1.1),(12.1, -1.1),(-14.3, -3.3),(-9.9, -3.3),(-5.5, -3.3),(-1.1, -3.3),(3.3, -3.3),(7.7, -3.3),(12.1, -3.3),(16.5, -3.3),(-14.3, -5.5),(-9.9, -5.5),(-5.5, -5.5),(-1.1, -5.5),(3.3, -5.5),(7.7, -5.5),(12.1, -5.5),(16.5, -5.5),(-14.3, -7.7),(-9.9, -7.7),(-5.5, -7.7),(-1.1, -7.7),(3.3, -7.7),(7.7, -7.7),(12.1, -7.7),(-14.3, -9.9),(-9.9, -9.9),(-5.5, -9.9),(-1.1, -9.9),(3.3, -9.9),(7.7, -9.9),(12.1, -9.9)]
mko2_points = [(-12.1, 31.9),(-9.9, 31.9),(-7.7, 31.9),(-5.5, 31.9),(-3.3, 31.9),(-1.1, 31.9),(1.1, 31.9),(3.3, 31.9),(5.5, 31.9),(7.7, 31.9),(9.9, 31.9),(12.1, 31.9),(-12.1, 29.7),(12.1, 29.7),(-18.7, 27.5),(-16.5, 27.5),(-14.3, 27.5),(-12.1, 27.5),(12.1, 27.5),(14.3, 27.5),(16.5, 27.5),(-18.7, 25.3),(18.7, 25.3),(-20.9, 23.1),(20.9, 23.1),(-20.9, 20.9),(20.9, 20.9),(-20.9, 18.7),(-14.3, 18.7),(14.3, 18.7),(20.9, 18.7),(-20.9, 16.5),(-18.7, 16.5),(-16.5, 16.5),(-14.3, 16.5),(14.3, 16.5),(16.5, 16.5),(18.7, 16.5),(20.9, 16.5),(-14.3, 14.3),(14.3, 14.3),(-16.5, 12.1),(16.5, 12.1),(-16.5, 9.9),(16.5, 9.9),(-18.7, 7.7),(18.7, 7.7),(-18.7, 5.5),(18.7, 5.5),(-20.9, 3.3),(20.9, 3.3),(23.1, 3.3),(-23.1, 1.1),(23.1, 1.1),(-23.1, -1.1),(23.1, -1.1),(-23.1, -3.3),(23.1, -3.3),(-23.1, -5.5),(23.1, -5.5),(-23.1, -7.7),(23.1, -7.7),(-23.1, -9.9),(23.1, -9.9),(-20.9, -12.1),(20.9, -12.1),(-18.7, -14.3),(18.7, -14.3),(-16.5, -16.5),(16.5, -16.5),(-14.3, -18.7),(-12.1, -18.7),(-9.9, -18.7),(-7.7, -18.7),(-5.5, -18.7),(5.5, -18.7),(7.7, -18.7),(9.9, -18.7),(12.1, -18.7),(14.3, -18.7),(-5.5, -20.9),(5.5, -20.9),(-5.5, -23.1),(5.5, -23.1),(-7.7, -25.3),(7.7, -25.3),(-9.9, -27.5),(9.9, -27.5),(12.1, -27.5),(-12.1, -29.7),(12.1, -29.7),(-12.1, -31.9),(-9.9, -31.9),(-7.7, -31.9),(-5.5, -31.9),(-3.3, -31.9),(-1.1, -31.9),(1.1, -31.9),(3.3, -31.9),(5.5, -31.9),(7.7, -31.9),(9.9, -31.9),(12.1, -31.9)]
As advised, I focused on using the Opentrons Python API in the Google Colab notebook as a starting point while building this script. I used the double helix DNA example code as a starting point.
Post-lab questions:
Find and describe a published paper that utilizes the Opentrons or an automation tool to achieve novel biological applications.
The study “Automation of protein crystallization scaleup via Opentrons-2 liquid handling” by Jacob DeRoo et al. demonstrates how a low-cost, general-purpose liquid handling robot, in this case the Opentrons OT-2, can be adapted to automate protein crystallization at multi-microliter scale. They designed a 3D-printed plate adapter and custom labware definitions so the robot could handle non-standard crystallization plates, and they wrote Python scripts to precisely control reservoir mixing and drop setup. The aforementioned authors then validated the system using mock dye gradients, hen egg white lysozyme, and an engineered Campylobacter jejuni protein that forms porous biomaterial crystals, showing that the OT-2 could reliably produce crystals while reducing hands-on labor and inter-operator variability. Although setup time was slightly longer than manual preparation, the approach they have designed for this paper was significantly more economical than dedicated crystallization robots and offers a flexible, programmable platform for scaling up crystal growth in structural biology and biomaterials.
Jacob B. DeRoo, Alec A. Jones, Caroline K. Slaughter, Tim W. Ahr, Sam M. Stroup, Grace B. Thompson, Christopher D. Snow, Automation of protein crystallization scaleup via Opentrons-2 liquid handling, SLAS Technology, Volume 32, 2025, 100268, ISSN 2472-6303, https://doi.org/10.1016/j.slast.2025.100268.
(https://www.sciencedirect.com/science/article/pii/S2472630325000263)
Write a description about what you intend to do with automation tools for your final project. You may include example pseudocode, Python scripts, 3D printed holders, a plan for how to use Ginkgo Nebula, and more. You may reference this week’s recitation slide deck for lab automation details.
For the biotechnology component of this project, which focuses on improving geometric isomer discrimination in ambergris biosynthesis, the Opentron platform functions as the core automation infrastructure for iterative enzyme screening and reaction optimization. The Opentrons system enables reproducible assembly of enzyme variant libraries in standardized microplate formats. By reducing variability in setup and analytical prep, the use of a machine like the OT-2 we were introduced to this week allows geometric isomer outcomes and overall product profile clarity for the perfume I would like to ultimately produce to be compared quantitatively across many potential conditions. This transforms the effort to improve three-dimensional molecular selectivity from a low-throughput enzymology problem into a structured design -> build -> test -> learn cycle supported by rich and reproducible data.
Similarly, for the materials and fragrance engineering component of my final project, which aims to create a self-modulating fragrance “system” with behavior that shifts with heat, moisture, and time on skin, the Opentrons liquid handling system enables systematic formulation prototyping and screening (mostly for behavior and adverse reactions when ready to test).