diff --git a/javascript/ComponentControllers.js b/javascript/ComponentControllers.js
new file mode 100644
index 000000000..194589c7a
--- /dev/null
+++ b/javascript/ComponentControllers.js
@@ -0,0 +1,257 @@
+/* This is a basic library that allows controlling elements that take some form of user input.
+
+This was previously written in typescript, where all controllers implemented an interface. Not
+all methods were needed in all the controllers, but it was done to keep a common interface, so
+your main app can serve as a controller of controllers.
+
+These controllers were built to work on the shapes of html elements that gradio components use.
+
+There may be some notes in it that only applied to my use case, but I left them to help others
+along.
+
+You will need the parent element for these to work.
+The parent element can be defined as the element (div) that gets the element id when assigning
+an element id to a gradio component.
+
+Example:
+ gr.TextBox(value="...", elem_id="THISID")
+
+Basic usage, grab an element that is the parent container for the component.
+
+Send it in to the class, like a function, don't forget the "new" keyword so it calls the constructor
+and sends back a new object.
+
+Example:
+
+let txt2imgPrompt = new TextComponentController(gradioApp().querySelector("#txt2img_prompt"))
+
+Then use the getVal() method to get the value, or use the setVal(myValue) method to set the value.
+
+Input types that are groups, like Checkbox groups (not individual checkboxes), take in an array of values.
+
+Checkbox group has to reset all values to False (unchecked), then set the values in your array to true (checked).
+If you don't hold a reference to the values (the labels in string format), you can acquire them using the getVal() method.
+*/
+class DropdownComponentController {
+ constructor(element) {
+ this.element = element;
+ this.childSelector = this.element.querySelector('select');
+ this.children = new Map();
+ Array.from(this.childSelector.querySelectorAll('option')).forEach(opt => this.children.set(opt.value, opt));
+ }
+ getVal() {
+ return this.childSelector.value;
+ }
+ updateVal(optionElement) {
+ optionElement.selected = true;
+ }
+ setVal(name) {
+ this.updateVal(this.children.get(name));
+ this.eventHandler();
+ }
+ eventHandler() {
+ this.childSelector.dispatchEvent(new Event("change"));
+ }
+}
+class CheckboxComponentController {
+ constructor(element) {
+ this.element = element;
+ this.child = this.element.querySelector('input');
+ }
+ getVal() {
+ return this.child.checked;
+ }
+ updateVal(checked) {
+ this.child.checked = checked;
+ }
+ setVal(checked) {
+ this.updateVal(checked);
+ this.eventHandler();
+ }
+ eventHandler() {
+ this.child.dispatchEvent(new Event("change"));
+ }
+}
+class CheckboxGroupComponentController {
+ constructor(element) {
+ this.element = element;
+ //this.checkBoxes = new Object;
+ this.children = new Map();
+ Array.from(this.element.querySelectorAll('input')).forEach(input => this.children.set(input.nextElementSibling.innerText, input));
+ /* element id gets use fieldset, grab all inputs (the bool val) get the userfriendly label, use as key, put bool value in mapping */
+ //Array.from(this.component.querySelectorAll("input")).forEach( _input => this.checkBoxes[_input.nextElementSibling.innerText] = _input)
+ /*Checkboxgroup structure
+