Param Panel

Number

Manual numeric text entry with optional bounds, plus first-class integer, positive, and float constraints.

Number is for manual numeric text entry — there is no track or thumb. For pointer-driven dragging, use Slider instead. min and max are optional, so Number suits unbounded values (iteration counts, durations) as well as bounded precise entry.

Try it

Usage

const iterations = pp.number({
  label: "Iterations",
  value: 10,
  min: 1,
  max: 100,
  step: 1,
});

Configuration

interface NumberConfig extends BaseControlConfig {
  value: number;

  min?: number;
  max?: number;
  step?: number;

  /** Restrict to whole numbers. Defaults to false (fractional values allowed). */
  integer?: boolean;

  /**
   * Restrict to positive values (> 0). Defaults to false. Composes with
   * `integer` — set both for a positive integer.
   */
  positive?: boolean;

  format?: (value: number) => string;
  parse?: (text: string) => number;
}

interface NumberControl extends ValueControl<number, NumberConfig> {}

Integers, positives, and floats

integer and positive are independent, composable flags rather than a single enum, so any combination is expressible:

// Whole numbers only, may be zero or negative.
const offset = pp.number({
  label: "Offset",
  value: 0,
  integer: true,
});

// Any positive real number.
const gain = pp.number({
  label: "Gain",
  value: 1.5,
  positive: true,
});

// Positive whole numbers only.
const particleCount = pp.number({
  label: "Particles",
  value: 1000,
  integer: true,
  positive: true,
});

// Unrestricted float — the default when neither flag is set.
const exposure = pp.number({
  label: "Exposure",
  value: 0.5,
});

Constraint violations follow the same out-of-range pattern as Slider:

  • Constructor and configure() throw when value violates integer, positive, min, or max.
  • User input is corrected on commit — rounded to the nearest integer when integer is set, clamped above zero when positive is set.
  • set() throws for a value violating any constraint rather than silently correcting it.

Optional limits

Because bounds are optional, you can remove one by clearing it through configure():

iterations.configure({ max: undefined }); // removes the upper bound

Custom parsing

parse converts typed text into a number; format renders the number back into display text. Together they let a field accept human-friendly input like 1.5s:

const duration = pp.number({
  label: "Duration",
  value: 500,
  min: 0,
  parse(text) {
    const normalized = text.trim().toLowerCase();

    if (normalized.endsWith("ms")) {
      return Number.parseFloat(normalized);
    }

    if (normalized.endsWith("s")) {
      return Number.parseFloat(normalized) * 1000;
    }

    return Number.parseFloat(normalized);
  },
  format(value) {
    return `${value} ms`;
  },
});

Typing 1.5s stores 1500; the field then displays 1500 ms.

Parsing errors are non-destructive

When parsing fails, the error is surfaced without immediately discarding the last valid value — users can correct their input without losing state.

On this page