diff --git a/src/features/conversion.ts b/src/features/conversion.ts
index 97637fa..f5146ca 100644
--- a/src/features/conversion.ts
+++ b/src/features/conversion.ts
@@ -1,11 +1,10 @@
 import type { OptionsFunc, Replace } from "features/feature";
 import { setDefault } from "features/feature";
 import type { Resource } from "features/resources/resource";
+import { InvertibleFormula } from "game/formulas/types";
 import type { BaseLayer } from "game/layers";
-import type { Modifier } from "game/modifiers";
 import type { DecimalSource } from "util/bignum";
 import Decimal from "util/bignum";
-import type { WithRequired } from "util/common";
 import type { Computable, GetComputableTypeWithDefault, ProcessedComputable } from "util/computed";
 import { convertComputable, processComputable } from "util/computed";
 import { createLazyProxy } from "util/proxies";
@@ -15,9 +14,10 @@ import { computed, unref } from "vue";
 /** An object that configures a {@link Conversion}. */
 export interface ConversionOptions {
     /**
-     * The scaling function that is used to determine the rate of conversion from one {@link features/resources/resource.Resource} to the other.
+     * The formula used to determine how much {@link gainResource} should be earned by this converting.
+     * When evaluating, the variable will always be overidden to the amount of {@link baseResource}.
      */
-    scaling: ScalingFunction;
+    formula: InvertibleFormula;
     /**
      * How much of the output resource the conversion can currently convert for.
      * Typically this will be set for you in a conversion constructor.
@@ -53,10 +53,6 @@ export interface ConversionOptions {
      * Defaults to true.
      */
     buyMax?: Computable<boolean>;
-    /**
-     * Whether or not to round up the cost to generate a given amount of the output resource.
-     */
-    roundUpCost?: Computable<boolean>;
     /**
      * The function that performs the actual conversion from {@link baseResource} to {@link gainResource}.
      * Typically this will be set for you in a conversion constructor.
@@ -73,20 +69,6 @@ export interface ConversionOptions {
      * This will not be called whenever using currentGain without calling convert (e.g. passive generation)
      */
     onConvert?: (amountGained: DecimalSource) => void;
-    /**
-     * An additional modifier that will be applied to the gain amounts.
-     * Must be reversible in order to correctly calculate {@link nextAt}.
-     * @see {@link game/modifiers.createSequentialModifier} if you want to apply multiple modifiers.
-     */
-    gainModifier?: WithRequired<Modifier, "revert">;
-    /**
-     * A modifier that will be applied to the cost amounts.
-     * That is to say, this modifier will be applied to the amount of baseResource before going into the scaling function.
-     * A cost modifier of x0.5 would give gain amounts equal to the player having half the baseResource they actually have.
-     * Must be reversible in order to correctly calculate {@link nextAt}.
-     * @see {@link game/modifiers.createSequentialModifier} if you want to apply multiple modifiers.
-     */
-    costModifier?: WithRequired<Modifier, "revert">;
 }
 
 /**
@@ -109,7 +91,6 @@ export type Conversion<T extends ConversionOptions> = Replace<
         nextAt: GetComputableTypeWithDefault<T["nextAt"], Ref<DecimalSource>>;
         buyMax: GetComputableTypeWithDefault<T["buyMax"], true>;
         spend: undefined extends T["spend"] ? (amountGained: DecimalSource) => void : T["spend"];
-        roundUpCost: GetComputableTypeWithDefault<T["roundUpCost"], true>;
     }
 >;
 
@@ -123,7 +104,6 @@ export type GenericConversion = Replace<
         nextAt: ProcessedComputable<DecimalSource>;
         buyMax: ProcessedComputable<boolean>;
         spend: (amountGained: DecimalSource) => void;
-        roundUpCost: ProcessedComputable<boolean>;
     }
 >;
 
@@ -142,11 +122,7 @@ export function createConversion<T extends ConversionOptions>(
 
         if (conversion.currentGain == null) {
             conversion.currentGain = computed(() => {
-                let gain = conversion.gainModifier
-                    ? conversion.gainModifier.apply(
-                          conversion.scaling.currentGain(conversion as GenericConversion)
-                      )
-                    : conversion.scaling.currentGain(conversion as GenericConversion);
+                let gain = conversion.formula.evaluate(conversion.baseResource.value);
                 gain = Decimal.floor(gain).max(0);
 
                 if (unref(conversion.buyMax) === false) {
@@ -160,17 +136,16 @@ export function createConversion<T extends ConversionOptions>(
         }
         if (conversion.currentAt == null) {
             conversion.currentAt = computed(() => {
-                let current = conversion.scaling.currentAt(conversion as GenericConversion);
-                if (unref((conversion as GenericConversion).roundUpCost))
-                    current = Decimal.ceil(current);
-                return current;
+                return conversion.formula.invert(
+                    Decimal.floor(unref((conversion as GenericConversion).currentGain))
+                );
             });
         }
         if (conversion.nextAt == null) {
             conversion.nextAt = computed(() => {
-                let next = conversion.scaling.nextAt(conversion as GenericConversion);
-                if (unref((conversion as GenericConversion).roundUpCost)) next = Decimal.ceil(next);
-                return next;
+                return conversion.formula.invert(
+                    Decimal.floor(unref((conversion as GenericConversion).currentGain)).add(1)
+                );
             });
         }
 
@@ -198,177 +173,11 @@ export function createConversion<T extends ConversionOptions>(
         processComputable(conversion as T, "nextAt");
         processComputable(conversion as T, "buyMax");
         setDefault(conversion, "buyMax", true);
-        processComputable(conversion as T, "roundUpCost");
-        setDefault(conversion, "roundUpCost", true);
 
         return conversion as unknown as Conversion<T>;
     });
 }
 
-/**
- * A collection of functions that allow a conversion to scale the amount of resources gained based on the input resource.
- * This typically shouldn't be created directly. Instead use one of the scaling function constructors.
- * @see {@link createLinearScaling}.
- * @see {@link createPolynomialScaling}.
- */
-export interface ScalingFunction {
-    /**
-     * Calculates the amount of the output resource a conversion should be able to currently produce.
-     * This should be based off of `conversion.baseResource.value`.
-     * The conversion is responsible for applying the gainModifier, so this function should be un-modified.
-     * It does not need to be clamped or rounded.
-     */
-    currentGain: (conversion: GenericConversion) => DecimalSource;
-    /**
-     * Calculates the amount of the input resource that is required for the current value of `conversion.currentGain`.
-     * Note that `conversion.currentGain` has been modified by `conversion.gainModifier`, so you will need to revert that as appropriate.
-     * The conversion is responsible for rounding up the amount as appropriate.
-     * The returned value should not be below 0.
-     */
-    currentAt: (conversion: GenericConversion) => DecimalSource;
-    /**
-     * Calculates the amount of the input resource that would be required for the current value of `conversion.currentGain` to increase.
-     * Note that `conversion.currentGain` has been modified by `conversion.gainModifier`, so you will need to revert that as appropriate.
-     * The conversion is responsible for rounding up the amount as appropriate.
-     * The returned value should not be below 0.
-     */
-    nextAt: (conversion: GenericConversion) => DecimalSource;
-}
-
-/**
- * Creates a scaling function based off the formula `(baseResource - base) * coefficient`.
- * If the baseResource value is less than base then the currentGain will be 0.
- * @param base The base variable in the scaling formula.
- * @param coefficient The coefficient variable in the scaling formula.
- * @example
- * A scaling function created via `createLinearScaling(10, 0.5)` would produce the following values:
- * | Base Resource | Current Gain |
- * | ------------- | ------------ |
- * | 10            | 1            |
- * | 12            | 2            |
- * | 20            | 6            |
- */
-export function createLinearScaling(
-    base: Computable<DecimalSource>,
-    coefficient: Computable<DecimalSource>
-): ScalingFunction {
-    const processedBase = convertComputable(base);
-    const processedCoefficient = convertComputable(coefficient);
-    return {
-        currentGain(conversion) {
-            let baseAmount: DecimalSource = unref(conversion.baseResource.value);
-            if (conversion.costModifier) {
-                baseAmount = conversion.costModifier.apply(baseAmount);
-            }
-            if (Decimal.lt(baseAmount, unref(processedBase))) {
-                return 0;
-            }
-
-            return Decimal.sub(baseAmount, unref(processedBase))
-                .sub(1)
-                .times(unref(processedCoefficient))
-                .add(1);
-        },
-        currentAt(conversion) {
-            let current: DecimalSource = unref(conversion.currentGain);
-            if (conversion.gainModifier) {
-                current = conversion.gainModifier.revert(current);
-            }
-            current = Decimal.max(0, current)
-                .sub(1)
-                .div(unref(processedCoefficient))
-                .add(unref(processedBase));
-            if (conversion.costModifier) {
-                current = conversion.costModifier.revert(current);
-            }
-            return current;
-        },
-        nextAt(conversion) {
-            let next: DecimalSource = Decimal.add(unref(conversion.currentGain), 1).floor();
-            if (conversion.gainModifier) {
-                next = conversion.gainModifier.revert(next);
-            }
-            next = Decimal.max(0, next)
-                .sub(1)
-                .div(unref(processedCoefficient))
-                .add(unref(processedBase))
-                .max(unref(processedBase));
-            if (conversion.costModifier) {
-                next = conversion.costModifier.revert(next);
-            }
-            return next;
-        }
-    };
-}
-
-/**
- * Creates a scaling function based off the formula `(baseResource / base) ^ exponent`.
- * If the baseResource value is less than base then the currentGain will be 0.
- * @param base The base variable in the scaling formula.
- * @param exponent The exponent variable in the scaling formula.
- * @example
- * A scaling function created via `createPolynomialScaling(10, 0.5)` would produce the following values:
- * | Base Resource | Current Gain |
- * | ------------- | ------------ |
- * | 10            | 1            |
- * | 40            | 2            |
- * | 250           | 5            |
- */
-export function createPolynomialScaling(
-    base: Computable<DecimalSource>,
-    exponent: Computable<DecimalSource>
-): ScalingFunction {
-    const processedBase = convertComputable(base);
-    const processedExponent = convertComputable(exponent);
-    return {
-        currentGain(conversion) {
-            let baseAmount: DecimalSource = unref(conversion.baseResource.value);
-            if (conversion.costModifier) {
-                baseAmount = conversion.costModifier.apply(baseAmount);
-            }
-            if (Decimal.lt(baseAmount, unref(processedBase))) {
-                return 0;
-            }
-
-            const gain = Decimal.div(baseAmount, unref(processedBase)).pow(
-                unref(processedExponent)
-            );
-
-            if (gain.isNan()) {
-                return new Decimal(0);
-            }
-            return gain;
-        },
-        currentAt(conversion) {
-            let current: DecimalSource = unref(conversion.currentGain);
-            if (conversion.gainModifier) {
-                current = conversion.gainModifier.revert(current);
-            }
-            current = Decimal.max(0, current)
-                .root(unref(processedExponent))
-                .times(unref(processedBase));
-            if (conversion.costModifier) {
-                current = conversion.costModifier.revert(current);
-            }
-            return current;
-        },
-        nextAt(conversion) {
-            let next: DecimalSource = Decimal.add(unref(conversion.currentGain), 1).floor();
-            if (conversion.gainModifier) {
-                next = conversion.gainModifier.revert(next);
-            }
-            next = Decimal.max(0, next)
-                .root(unref(processedExponent))
-                .times(unref(processedBase))
-                .max(unref(processedBase));
-            if (conversion.costModifier) {
-                next = conversion.costModifier.revert(next);
-            }
-            return next;
-        }
-    };
-}
-
 /**
  * Creates a conversion that simply adds to the gainResource amount upon converting.
  * This is similar to the behavior of "normal" layers in The Modding Tree.
@@ -396,13 +205,8 @@ export function createIndependentConversion<S extends ConversionOptions>(
 
         if (conversion.currentGain == null) {
             conversion.currentGain = computed(() => {
-                let gain = conversion.gainModifier
-                    ? conversion.gainModifier.apply(
-                          conversion.scaling.currentGain(conversion as GenericConversion)
-                      )
-                    : conversion.scaling.currentGain(conversion as GenericConversion);
+                let gain = conversion.formula.evaluate(conversion.baseResource.value);
                 gain = Decimal.floor(gain).max(conversion.gainResource.value);
-
                 if (unref(conversion.buyMax) === false) {
                     gain = gain.min(Decimal.add(conversion.gainResource.value, 1));
                 }
@@ -412,7 +216,7 @@ export function createIndependentConversion<S extends ConversionOptions>(
         if (conversion.actualGain == null) {
             conversion.actualGain = computed(() => {
                 let gain = Decimal.sub(
-                    Decimal.floor(conversion.scaling.currentGain(conversion as GenericConversion)),
+                    conversion.formula.evaluate(conversion.baseResource.value),
                     conversion.gainResource.value
                 ).max(0);
 
@@ -424,11 +228,7 @@ export function createIndependentConversion<S extends ConversionOptions>(
         }
         setDefault(conversion, "convert", function () {
             const amountGained = unref((conversion as GenericConversion).actualGain);
-            conversion.gainResource.value = conversion.gainModifier
-                ? conversion.gainModifier.apply(
-                      unref((conversion as GenericConversion).currentGain)
-                  )
-                : unref((conversion as GenericConversion).currentGain);
+            conversion.gainResource.value = unref((conversion as GenericConversion).currentGain);
             (conversion as GenericConversion).spend(amountGained);
             conversion.onConvert?.(amountGained);
         });
@@ -466,71 +266,3 @@ export function setupPassiveGeneration(
         }
     });
 }
-
-/**
- * Given a value, this function finds the amount above a certain value and raises it to a power.
- * If the power is <1, this will effectively make the value scale slower after the cap.
- * @param value The raw value.
- * @param cap The value after which the softcap should be applied.
- * @param power The power to raise value above the cap to.
- * @example
- * A softcap added via `addSoftcap(scaling, 100, 0.5)` would produce the following values:
- * | Raw Value | Softcapped Value |
- * | --------- | ---------------- |
- * | 1         | 1                |
- * | 100       | 100              |
- * | 125       | 105              |
- * | 200       | 110              |
- */
-export function softcap(
-    value: DecimalSource,
-    cap: DecimalSource,
-    power: DecimalSource = 0.5
-): DecimalSource {
-    if (Decimal.lte(value, cap)) {
-        return value;
-    } else {
-        return Decimal.pow(value, power).times(Decimal.pow(cap, Decimal.sub(1, power)));
-    }
-}
-
-/**
- * Creates a scaling function based off an existing scaling function, with a softcap applied to it.
- * The softcap will take any value above a certain value and raise it to a power.
- * If the power is <1, this will effectively make the value scale slower after the cap.
- * @param scaling The raw scaling function.
- * @param cap The value after which the softcap should be applied.
- * @param power The power to raise value about the cap to.
- * @see {@link softcap}.
- */
-export function addSoftcap(
-    scaling: ScalingFunction,
-    cap: ProcessedComputable<DecimalSource>,
-    power: ProcessedComputable<DecimalSource> = 0.5
-): ScalingFunction {
-    return {
-        ...scaling,
-        currentAt: conversion =>
-            softcap(scaling.currentAt(conversion), unref(cap), Decimal.recip(unref(power))),
-        nextAt: conversion =>
-            softcap(scaling.nextAt(conversion), unref(cap), Decimal.recip(unref(power))),
-        currentGain: conversion =>
-            softcap(scaling.currentGain(conversion), unref(cap), unref(power))
-    };
-}
-
-/**
- * Creates a scaling function off an existing function, with a hardcap applied to it.
- * The harcap will ensure that the currentGain will stop at a given cap.
- * @param scaling The raw scaling function.
- * @param cap The maximum value the scaling function can output.
- */
-export function addHardcap(
-    scaling: ScalingFunction,
-    cap: ProcessedComputable<DecimalSource>
-): ScalingFunction {
-    return {
-        ...scaling,
-        currentGain: conversion => Decimal.min(scaling.currentGain(conversion), unref(cap))
-    };
-}
diff --git a/src/game/formulas/formulas.ts b/src/game/formulas/formulas.ts
index 12ea545..9806580 100644
--- a/src/game/formulas/formulas.ts
+++ b/src/game/formulas/formulas.ts
@@ -315,6 +315,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
         return new Formula({ variable: value }) as InvertibleFormula;
     }
 
+    // TODO add integration support to step-wise functions
     /**
      * Creates a step-wise formula. After {@ref start} the formula will have an additional modifier.
      * This function assumes the incoming {@ref value} will be continuous and monotonically increasing.