As part of this exploratory study, I used ChatGPT to assist in building new IGB apps and assess the ease of development using LLM-based tools.
Experience Summary: I started by discussing the “super-simple-igb-app” structure with ChatGPT. After confirming it was a minimal viable starter app for the Integrated Genome Browser, I asked:
- Whether users can use LLMs like ChatGPT to create IGB apps.
- How we can empower the public to create IGB plugins using AI.
- If ChatGPT could generate a working example IGB plugin for R&D purposes.
ChatGPT provided detailed and relevant responses, suggesting multiple ways users could be supported via ChatGPT APIs and even proposed a plugin idea that displays a random genomics-related fact.
I implemented the suggested app, and it worked successfully. The total time to set up, test, and verify this app was approximately 1 hour.
Successful Outcome
App Created: Displays a random genomics fact or quote upon clicking a menu item
Unsuccessful Attempt: Next, I attempted to build a more complex app using ChatGPT: one that would take screenshots at user-defined intervals and compile them into a PDF.
ChatGPT generated relevant code using PDDocument and PDImageXObject.
However, the app failed to run in IGB, likely due to OSGi integration limitations.
I spent 2–3 hours debugging but was ultimately unsuccessful in getting it to work within the IGB environment.
Limitations: Complex apps requiring interaction with threads or background tasks still demand deeper knowledge of IGB internals and OSGi.
Potential: With proper scaffolding tools or a custom GPT trained on IGB-specific documentation and examples, even non-expert developers could create functional IGB apps more easily.
Artifacts Provided
Working code for genomics fact plugin
Prototype code for screenshot recorder plugin (non-functional)
Facts app -
package org.bioviz.igb.app;
import org.osgi.service.component.annotations.Component;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Random;
import javax.swing.JOptionPane;
import org.lorainelab.igb.menu.api.MenuBarEntryProvider;
import org.lorainelab.igb.menu.api.model.MenuBarParentMenu;
import org.lorainelab.igb.menu.api.model.MenuItem;
@Component(immediate = true)
public class GenomicsFunApp implements MenuBarEntryProvider {
private final List<String> facts = Arrays.asList(
"Did you know? The human genome contains about 3 billion base pairs.",
"Fun Fact: Mitochondrial DNA is inherited only from the mother.",
"Trivia: Only 1.5% of human DNA codes for proteins!",
"The first sequenced genome was that of a virus, in 1976."
);
@Override
public Optional<List<MenuItem>> getMenuItems() {
MenuItem menuItem = new MenuItem("Random Genome Fact", (Void t) -> {
Random rand = new Random();
String fact = facts.get(rand.nextInt(facts.size()));
JOptionPane.showMessageDialog(null, fact);
return t;
});
menuItem.setWeight(1000000000);
return Optional.ofNullable(Arrays.asList(menuItem));
}
@Override
public MenuBarParentMenu getMenuExtensionParent() {
return MenuBarParentMenu.TOOLS;
}
}
Screenrecorder app -
package org.bioviz.igb.app;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.osgi.service.component.annotations.Component;
import org.lorainelab.igb.menu.api.MenuBarEntryProvider;
import org.lorainelab.igb.menu.api.model.MenuBarParentMenu;
import org.lorainelab.igb.menu.api.model.MenuItem;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.List;
import java.util.concurrent.*;
@Component(immediate = true)
public class ScreenshotRecorderApp implements MenuBarEntryProvider {
private ScheduledExecutorService scheduler;
private List<File> screenshots = new ArrayList<>();
private File outputDir = new File(System.getProperty("user.home"), "IGBSnapshots");
@Override
public Optional<List<MenuItem>> getMenuItems() {
MenuItem startItem = new MenuItem("Start Screenshot Recorder", (Void t) -> {
String input = JOptionPane.showInputDialog("Enter interval (in seconds):");
if (input == null) return t;
try {
int interval = Integer.parseInt(input.trim());
startRecording(interval);
JOptionPane.showMessageDialog(null, "Recording started. Use 'Stop Screenshot Recorder' to stop.");
} catch (NumberFormatException e) {
JOptionPane.showMessageDialog(null, "Invalid input. Please enter a number.");
}
return t;
});
MenuItem stopItem = new MenuItem("Stop Screenshot Recorder", (Void t) -> {
stopRecording();
return t;
});
return Optional.of(Arrays.asList(startItem, stopItem));
}
private void startRecording(int intervalSeconds) {
if (!outputDir.exists()) outputDir.mkdirs();
screenshots.clear();
scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(() -> {
try {
Robot robot = new Robot();
Rectangle screenRect = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
BufferedImage screenFullImage = robot.createScreenCapture(screenRect);
File file = new File(outputDir, "screenshot_" + System.currentTimeMillis() + ".png");
ImageIO.write(screenFullImage, "png", file);
screenshots.add(file);
System.out.println("Screenshot taken: " + file.getAbsolutePath());
} catch (Exception e) {
e.printStackTrace();
}
}, 0, intervalSeconds, TimeUnit.SECONDS);
}
private void stopRecording() {
if (scheduler != null) {
scheduler.shutdown();
try {
if (!scheduler.awaitTermination(2, TimeUnit.SECONDS)) {
scheduler.shutdownNow();
}
} catch (InterruptedException e) {
scheduler.shutdownNow();
}
}
try {
File pdfFile = new File(outputDir, "IGB_Screenshots_" + System.currentTimeMillis() + ".pdf");
PDDocument doc = new PDDocument();
for (File imageFile : screenshots) {
BufferedImage bimg = ImageIO.read(imageFile);
PDPage page = new PDPage(new PDRectangle(bimg.getWidth(), bimg.getHeight()));
doc.addPage(page);
PDImageXObject pdImage = LosslessFactory.createFromImage(doc, bimg);
PDPageContentStream contentStream = new PDPageContentStream(doc, page);
contentStream.drawImage(pdImage, 0, 0);
contentStream.close();
}
doc.save(pdfFile);
doc.close();
JOptionPane.showMessageDialog(null, "PDF created: " + pdfFile.getAbsolutePath());
} catch (IOException e) {
JOptionPane.showMessageDialog(null, "Error creating PDF: " + e.getMessage());
}
}
@Override
public MenuBarParentMenu getMenuExtensionParent() {
return MenuBarParentMenu.TOOLS;
}
}
Attached PDF contains the prompt questions and my responses generated using ChatGPT.
Prompts-IGB-Apps-R&D.pdf
Link to repository for Screenrecorder App - https://bitbucket.org/pranavbhatia1999/screenrecorder-app/src/master/
Link to repository for Genomics Fun Facts App - https://bitbucket.org/pranavbhatia1999/genomics-fun-app/src/master/