diff --git a/src/MRIBuilder.jl b/src/MRIBuilder.jl
index 281f3c0f4e0152effc7ac14ad35b5c4165e6bba2..f43eb7a93148fb54e6c12701486b0e59c37a9c68 100644
--- a/src/MRIBuilder.jl
+++ b/src/MRIBuilder.jl
@@ -3,40 +3,48 @@ Builds and optimises NMR/MRI sequences.
 """
 module MRIBuilder
 
+include("global_model.jl")
 include("scanners.jl")
+include("variables.jl")
 include("building_blocks.jl")
-include("sequence_builders.jl")
 include("concrete_blocks.jl")
 include("wait.jl")
+include("containers/containers.jl")
 include("gradients/gradients.jl")
 include("pulses/pulses.jl")
 include("readouts/readouts.jl")
 
-import .BuildingBlocks: BuildingBlock, scanner_constraints!
-export BuildingBlock, scanner_constraints!
+import .GlobalModel: set_model
+export set_model
 
-import .SequenceBuilders: SequenceBuilder, start_time, end_time, duration, TR
-export SequenceBuilder, start_time, end_time, duration, TR
+import .Scanners: Scanner, B0, Siemens_Connectom, Siemens_Prisma, Siemens_Terra
+export Scanner, B0, Siemens_Connectom, Siemens_Prisma, Siemens_Terra
 
-import .ConcreteBlocks: ConcreteBlock, Sequence
-export ConcreteBlock, Sequence
+import .Variables: variables, duration, start_time, end_time, flip_angle, amplitude, phase, frequency, bandwidth, N_left, N_right, qval, area_under_curve, δ, rise_time, flat_time, slew_rate, gradient_strength
+export variables, duration, start_time, end_time, flip_angle, amplitude, phase, frequency, bandwidth, N_left, N_right, qval, area_under_curve, δ, rise_time, flat_time, slew_rate, gradient_strength
+
+import .BuildingBlocks: BuildingBlock
+export BuildingBlocks
+
+import .ConcreteBlocks: ConcreteBlock, AbstractConcreteBlock
+export ConcreteBlocks, AbstractConcreteBlock
 
 import .Wait: WaitBlock
 export WaitBlock
 
-import .Gradients: PulsedGradient, InstantGradientBlock, qval, rise_time, flat_time, slew_rate, gradient_strength, bval
-export PulsedGradient, InstantGradientBlock, qval, rise_time, flat_time, slew_rate, gradient_strength, bval
+import .Containers: Sequence
+export Sequence
 
-import .Pulses: InstantRFPulseBlock, ConstantPulse, SincPulse, flip_angle, phase, frequency, bandwidth, N_left, N_right
-export InstantRFPulseBlock, ConstantPulse, SincPulse, flip_angle, phase, frequency, bandwidth, N_left, N_right
+import .Gradients: PulsedGradient, InstantGradientBlock
+export PulsedGradient, InstantGradientBlock
+
+import .Pulses: InstantRFPulseBlock, ConstantPulse, SincPulse
+export InstantRFPulseBlock, ConstantPulse, SincPulse
 
 import .Readouts: InstantReadout
 export InstantReadout
 
-import .Scanners: Scanner, Siemens_Connectom, Siemens_Prisma, Siemens_Terra
-export Scanner, Siemens_Connectom, Siemens_Prisma, Siemens_Terra
-
-using JuMP
+import JuMP: @constraint, @objective, objective_function, optimize!, has_values, value
 export @constraint, @objective, objective_function, optimize!, has_values, value
 
 end
diff --git a/src/building_blocks.jl b/src/building_blocks.jl
index d9b0374dbe6b122ceca451a32849079a72d57589..715092b4a7aaa25bd097087d410daa5438cd9e06 100644
--- a/src/building_blocks.jl
+++ b/src/building_blocks.jl
@@ -1,44 +1,36 @@
 module BuildingBlocks
 import JuMP: has_values, GenericVariableRef, value, Model, @constraint, @objective, owner_model, objective_function
 import Printf: @sprintf
-import ..Scanners: Scanner, gradient_strength, slew_rate
+import ..Scanners: Scanner
+import ..Variables: variables, start_time, duration, end_time, gradient_strength, slew_rate
 
 """
 Parent type for all individual components out of which a sequence can be built.
 
 Required methods:
 - [`duration`](@ref)(block, parameters): returns block duration in ms.
-- [`to_concrete_block`](@ref)(sequence, block): converts the block into a `ConcreteBlock`, which will be part of given `Sequence`.
+- `to_concrete_block`(sequence, block): converts the block into a `ConcreteBlock`, which will be part of given `Sequence`.
 - [`variables`](@ref): A list of all functions that are used to compute variables of the building block. Any of these can be used in constraints or objective functions.
 """
 abstract type BuildingBlock end
 
 
 """
-    duration(building_block)
+    start_time(container, args...)
 
-The duration of the building block in ms.
+Returns the starting time of the specific [`BuildingBlock`](@ref) within the container.
+The [`BuildingBlock`](@ref) is defined by one or more indices as defined below.
 """
-function duration end
+start_time(bb) = 0.
 
-
-"""
-    gradient_strength(gradient)
-
-Maximum gradient strength in kHz/um.
-
-If a [`Scanner`](@ref) is provided, this will be constrained to be lower than the maximum scanner gradient strength.
 """
-function gradient_strength end
+    end_time(container, args...)
 
+Returns the end time of the specific [`BuildingBlock`](@ref) within the container.
+The [`BuildingBlock`](@ref) is defined by one or more indices as defined below.
 """
-    slew_rate(gradient)
+end_time(bb) = duration(bb)
 
-Maximum rate of increase (and decrease) of the gradient strength in kHz/um/ms.
-
-If a [`Scanner`](@ref) is provided, this will be constrained to be lower than the maximum scanner slew rate.
-"""
-function slew_rate end
 
 """
     scanner_constraints!([model, ]building_block, scanner)
@@ -65,24 +57,14 @@ Returns a list of function that can be called to constrain the `building_block`.
 """
 variables(bb::BuildingBlock) = variables(typeof(bb))
 
-struct _BuildingBlockPrinter
-    bb :: BuildingBlock
-    number :: Integer
-end
 
 function Base.show(io::IO, block::BuildingBlock)
     print(io, string(typeof(block)), "(")
-    if has_values(block)
-        if iszero(value(duration(block)))
-            print(io, "time=", @sprintf("%.3f", value(start_time(block))), ", ")
-        else
-            print(
-                io, "time=", 
-                @sprintf("%.3f", value(start_time(block))), " - ",
-                @sprintf("%.3f", value(end_time(block))), ", "
-            )
-        end
-    end
+    internal_print(io, block)
+    print(io, ")")
+end
+
+function internal_print(io::IO, block::BuildingBlock)
     for name in propertynames(block)
         value = getproperty(block, name)
         if value isa GenericVariableRef || name == :parent || string(name)[1] == '_'
@@ -101,14 +83,12 @@ function Base.show(io::IO, block::BuildingBlock)
             print(io, "$(nameof(fn))=$(printed_value), ")
         end
     end
-    print(io, ")")
 end
 
 # The `start_time` and `end_time` functions are properly defined in "sequence_builders.jl"
 function start_time end
 function end_time end
 
-
 """
     set_simple_constraints!(model, block, kwargs)
 
@@ -158,42 +138,4 @@ function match_blocks!(block1::BuildingBlock, block2::BuildingBlock)
     match_blocks!(block1, block2, property_list)
 end
 
-
-"""
-Stores the parameters passed on a [`BuildingBlock`](@ref) constructor (of type `T`).
-
-The parameters are temporarily stored in this format, until they can be added to a `SequenceBuilder`.
-
-For example, the following
-```julia
-pg = PulsedGradient(qval=2)
-```
-will return a `BuildingBlockPlaceholder` object rather than a `PulsedGradient` object.
-Only when this object is added to a `SequenceBuilder`, is the `PulsedGradient` actually initialised using the JuMP model of that `SequenceBuilder`:
-```julia
-sb = SequenceBuilder([pg])
-```
-You can access the initialised `PulsedGradient` through the `BuildingBlockPlaceholder` (`pg.concrete[]`) or directly through the `SequenceBuilder` (`sb[1]`)
-
-Each Placeholder can be added to only a single `SequenceBuilder`, but it can be added multiple times to the same `SequenceBuilder`.
-If added multiple times to the same `SequenceBuilder`, all variables will be matched between them.
-"""
-struct BuildingBlockPlaceholder{T<:BuildingBlock}
-    args
-    kwargs
-    concrete :: Ref{T}
-    BuildingBlockPlaceholder{T}(args...; kwargs...) where {T<:BuildingBlock} = new{T}(args, kwargs, Ref{T}())
-end
-
-function Base.show(io::IO, placeholder::BuildingBlockPlaceholder{T}) where {T}
-    if isassigned(placeholder.concrete)
-        print(io, "Assigned BuildingBlockPlaceholder for $(placeholder.concrete[])")
-    else
-        args = join(placeholder.args, ", ")
-        kwargs = join(["$key=$value" for (key, value) in pairs(placeholder.kwargs)], ", ")
-        print(io, "Unassigned BuildingBlockPlaceholder{$T}($args; $kwargs)")
-    end
-end
-
-
 end
\ No newline at end of file
diff --git a/src/concrete_blocks.jl b/src/concrete_blocks.jl
index 539b8b4f1709b02d300265e622776ed3e2c3cd59..19528ac25b1de660a39e3aab87129607e814a08c 100644
--- a/src/concrete_blocks.jl
+++ b/src/concrete_blocks.jl
@@ -1,9 +1,8 @@
 module ConcreteBlocks
 import JuMP: has_values, optimize!, value
-import ..BuildingBlocks: BuildingBlock, BuildingBlockPlaceholder, properties, duration
-import ..SequenceBuilders: SequenceBuilder, to_block, AbstractSequence, TR, get_blocks
+import ..Variables: variables, duration
+import ..BuildingBlocks: BuildingBlock
 
-abstract type AbstractConcreteBlock <: BuildingBlock end
 
 struct ConcreteRFPulse
     time :: Vector{Number}
@@ -64,13 +63,14 @@ ConcreteGradient(values::Tuple{<:Vector, <:Vector}) = ConcreteGradient(values...
 ConcreteGradient(values::Tuple{<:Vector, <:Vector, <:Vector, <:Vector}) = ConcreteGradient(values...)
 
 
+abstract type AbstractConcreteBlock <: BuildingBlock end
+
 """
     ConcreteBlock(duration; pulse=nothing, gradient=nothing, rotate_bvec=false, readout_times=nothing)
 
 A [`BuildingBlock`](@ref) that is fully defined (i.e., there are no variables to be optimised).
 """
 struct ConcreteBlock <: AbstractConcreteBlock
-    builder :: AbstractSequence
     duration :: Float64
     pulse :: Union{ConcreteRFPulse, Nothing}
     gradient :: Union{ConcreteGradient, Nothing}
@@ -78,31 +78,29 @@ struct ConcreteBlock <: AbstractConcreteBlock
     rotate_gradient :: Bool
 end
 
-ConcreteBlock(args...; kwargs...) = BuildingBlockPlaceholder{ConcreteBlock}(args...; kwargs...)
-
-function ConcreteBlock(builder::AbstractSequence, duration::Number; pulse=nothing, gradient=nothing, readout_times=Number[], rotate_gradient=false)
-    ConcreteBlock(builder, duration, ConcreteRFPulse(pulse), ConcreteGradient(gradient), Float64.(readout_times), rotate_gradient)
+function ConcreteBlock(duration::Number; pulse=nothing, gradient=nothing, readout_times=Number[], rotate_gradient=false)
+    ConcreteBlock(duration, ConcreteRFPulse(pulse), ConcreteGradient(gradient), Float64.(readout_times), rotate_gradient)
 end
 
 
-has_values(c::AbstractConcreteBlock) = has_values(c.builder)
+has_values(c::AbstractConcreteBlock) = true
 duration(c::AbstractConcreteBlock) = 0.
 duration(c::ConcreteBlock) = c.duration
 
 
 """
-    ConcreteBlock(sequence, other_building_block)
+    ConcreteBlock(other_building_block)
 
 Creates a [`ConcreteBlock`](@ref) from another [`BuildingBlock`](@ref).
 
 This will raise an error if the other [`BuildingBlock`](@ref) has not been optimised yet.
 If it has been optimised, then [`to_concrete_block`](@ref) will be called.
 """
-function ConcreteBlock(sequence::AbstractSequence, block::BuildingBlock)
+function ConcreteBlock(block::BuildingBlock)
     if !has_values(block)
         error("Making `BuildingBlock` objects concrete is only possible after optimisation.")
     end
-    return to_concrete_block(sequence, block)
+    return to_concrete_block(block)
 end
 
 
@@ -113,40 +111,11 @@ Internal function used to create [`ConcreteBlock`](@ref) from any [`BuildingBloc
 
 This needs to be defined for every [`BuildingBlock`](@ref)
 """
-function to_concrete_block(sequence::AbstractSequence, cb::ConcreteBlock)
-    return ConcreteBlock(sequence, cb.duration, cb.pulse, cb.gradient, cb.readout_times)
+function to_concrete_block(cb::ConcreteBlock)
+    return ConcreteBlock(cb.duration, cb.pulse, cb.gradient, cb.readout_times, cb.rotate_gradient, value.(cb.start_time))
 end
 
 variables(::Type{<:AbstractConcreteBlock}) = []
 
 
-"""
-    Sequence(builder::SequenceBuilder)
-
-A fully defined sequence with no free variables.
-
-When created from a [`SequenceBuilder`](@ref), all non-fixed variables are optimised given any constraints
-and the resulting sequence is returned.
-"""
-struct Sequence <: AbstractSequence
-    blocks :: Vector{<:AbstractConcreteBlock}
-    TR :: Number
-end
-
-TR(seq::Sequence) = seq.TR
-get_blocks(seq::Sequence) = seq.blocks
-has_values(seq::Sequence) = true
-
-function Sequence(builder::SequenceBuilder)
-    if !has_values(builder)
-        optimize!(builder.model)
-    end
-    seq = Sequence(AbstractConcreteBlock[], value(TR(builder)))
-    for block in builder.blocks
-        @show block
-        push!(seq.blocks, ConcreteBlock(seq, block))
-    end
-    return seq
-end
-
 end
\ No newline at end of file
diff --git a/src/containers/containers.jl b/src/containers/containers.jl
new file mode 100644
index 0000000000000000000000000000000000000000..591b9f09a426b9d327daf5bd879a95c9efb929e9
--- /dev/null
+++ b/src/containers/containers.jl
@@ -0,0 +1,5 @@
+module Containers
+include("sequences.jl")
+
+import .Sequences: Sequence
+end
\ No newline at end of file
diff --git a/src/containers/sequences.jl b/src/containers/sequences.jl
new file mode 100644
index 0000000000000000000000000000000000000000..5dc31a06ebbb64f7d8740f2dde94acb682025744
--- /dev/null
+++ b/src/containers/sequences.jl
@@ -0,0 +1,55 @@
+module Sequences
+import JuMP: Model
+import ...GlobalModel: @global_model_constructor
+import ...Variables: variables, start_time, duration, VariableType
+import ...BuildingBlocks: BuildingBlock
+import ...InsertedBuildingBlocks: InsertedBuildingBlock
+
+"""
+    Sequence(building_blocks...)
+    Sequence([building_blocks])
+
+Represents a series of [`BuildingBlock`](@ref) objects run in turn.
+"""
+struct Sequence <: BuildingBlock
+    model :: Model
+    blocks :: Vector{<:BuildingBlock}
+end
+
+@global_model_constructor Sequence
+
+Sequence(model::Model, blocks...) = Sequence(model, blocks)
+
+Base.length(seq::Sequence) = length(seq)
+Base.getindex(seq::Sequence, index) = seq[index]
+
+"""
+    start_time(sequence::Sequence, index::Integer, args...)
+
+Returns the starting time of the [`BuildingBlock`](@ref) with index `index`.
+Additional `args` can be used to select a sub-block of that [`BuildingBlock`](@ref).
+The starting time is returned with respect to the start of this sequence.
+"""
+start_time(seq::Sequence) = 0.
+start_time(seq::Sequence, index::Integer) = iszero(index) ? start_time(seq) : (start_time(seq, index-1) + duration(seq[index]))
+start_time(seq::Sequence, index::Integer, args...) = start_time(seq, index) + start_time(seq[index], args...)
+
+"""
+    end_time(sequence::Sequence, index::Integer, args...)
+
+Returns the end time of the [`BuildingBlock`](@ref) with index `index`.
+Additional `args` can be used to select a sub-block of that [`BuildingBlock`](@ref).
+The end time is returned with respect to the start of this sequence.
+"""
+end_time(seq::Sequence, index::Integer) = start_time(seq, index) + duration(seq[index])
+end_time(seq::Sequence, index::Integer, args...) = start_time(seq, index) + end_time(seq[index], args...)
+
+duration(seq::Sequence) = end_time(seq, length(seq))
+
+
+
+end
+
+
+
+
diff --git a/src/global_model.jl b/src/global_model.jl
new file mode 100644
index 0000000000000000000000000000000000000000..d845afb46f6334eafeb55bca3a55b1b79884b71b
--- /dev/null
+++ b/src/global_model.jl
@@ -0,0 +1,63 @@
+module GlobalModel
+import JuMP: Model
+
+const GLOBAL_MODEL = Ref(Model())
+const IGNORE_MODEL = GLOBAL_MODEL[]
+
+"""
+Sets a global JuMP `Model`.
+
+Use as 
+```julia
+model = set_model([model]) do
+    ...
+end
+```
+Any sequences created within the code block will be assigned the same JuMP `Model`.
+If no model is provided a new one is created (using a Juniper optimizer based on the Ipopt non-linear optimizer).
+
+The function will return the fully formed JuMP `Model`.
+"""
+function set_model(f::Function, model::Model)
+    prev_model = global_model[]
+    global_model[] = model
+    try
+        f()
+    finally
+        global_model[] = prev_model
+    end
+    return model
+end
+
+function set_model(f::Function)
+    ipopt_opt = optimizer_with_attributes(Ipopt.Optimizer, "print_level" => 0)
+    juniper_opt = optimizer_with_attributes(Juniper.Optimizer, "nl_solver" => ipopt_opt)
+    model = Model(juniper_opt)
+    set_model(f, model)
+end
+
+
+function get_global_model()
+    if GLOBAL_MODEL[] == IGNORE_MODEL
+        error("No global model has been set. Please explicitly set one in the constructor or set a global model using `set_model`.")
+    end
+    return GLOBAL_MODEL[]
+end
+
+
+"""
+    @global_model_constructor BuildingBlockType
+
+Add a onstructor the [`BuildingBlock`](@ref) subtype that fetches the global JuMP model (set by [`set_model`](@ref)) and assigns it to the first argument.
+```
+BuildingBlockType(args...; kwargs...) = BuildingBlockType(global_model::JuMP.Model, args...; kwargs...)
+```
+"""
+macro global_model_constructor(bb)
+    quote
+        $(esc(bb))(args...; kwargs...) = $(esc(bb))(get_global_model(), args...; kwargs...)
+    end
+end
+
+
+end
\ No newline at end of file
diff --git a/src/gradients/gradients.jl b/src/gradients/gradients.jl
index 907cc8e3c832a2c4930a93506fd86da082a96d89..8c6dbb6f64a21323e34cc8dda586a9a276e403d2 100644
--- a/src/gradients/gradients.jl
+++ b/src/gradients/gradients.jl
@@ -3,7 +3,6 @@ include("integrate_gradients.jl")
 include("pulsed_gradients.jl")
 include("instant_gradients.jl")
 
-import .IntegrateGradients: qval, bval
-import .PulsedGradients: PulsedGradient, rise_time, flat_time, slew_rate, gradient_strength
+import .PulsedGradients: PulsedGradient
 import .InstantGradients: InstantGradientBlock
 end
\ No newline at end of file
diff --git a/src/gradients/instant_gradients.jl b/src/gradients/instant_gradients.jl
index a8ee301d74714cde249c9eb49a295d9a78e05fc1..22016ec190c3dd226f5f423532f31ca3aabf6a22 100644
--- a/src/gradients/instant_gradients.jl
+++ b/src/gradients/instant_gradients.jl
@@ -1,9 +1,9 @@
 module InstantGradients
-import JuMP: @constraint, @variable, VariableRef
-import ...BuildingBlocks: BuildingBlock, properties, BuildingBlockPlaceholder, set_simple_constraints!, duration
-import ...SequenceBuilders: SequenceBuilder, owner_model, start_time
-import ...ConcreteBlocks: to_concrete_block, AbstractConcreteBlock, Sequence, AbstractSequence
-import ..IntegrateGradients: qval, bval
+import JuMP: @constraint, @variable, Model, owner_model
+import ...Variables: qval, bval, start_time, duration, variables, get_free_variable, VariableType
+import ...BuildingBlocks: BuildingBlock
+import ...ConcreteBlocks: to_concrete_block, AbstractConcreteBlock
+import ...GlobalModel: @global_model_constructor
 
 """
     InstantGradientBlock(; orientation=:bvec, qval=nothing)
@@ -19,21 +19,20 @@ This is a [`BuildingBlock`](@ref) for the [`SequenceBuilder`](@ref).
 - [`qval`](@ref): Spatial scale on which spins will be dephased due to this pulsed gradient in rad/um.
 """
 struct InstantGradientBlock <: BuildingBlock
-    builder::SequenceBuilder
+    model::Model
     orientation :: Any
-    qval :: VariableRef
+    qval :: VariableType
 end
 
-InstantGradientBlock(; kwargs...) = BuildingBlockPlaceholder{InstantGradientBlock}(; kwargs...)
+@global_model_constructor InstantGradientBlock
 
-function InstantGradientBlock(builder::SequenceBuilder; orientation=:bvec, kwargs...)
-    model = owner_model(builder)
+function InstantGradientBlock(model::Model; orientation=:bvec, qval=nothing)
     res = InstantGradientBlock(
-        builder,
+        model,
         orientation,
-        @variable(model)
+        get_free_variable(model, qval),
     )
-    set_simple_constraints!(model, res, kwargs)
+    @constraint model model.qval >= 0
     return res
 end
 
@@ -51,13 +50,12 @@ Instantaneous MR gradient with no free variables.
 See [`InstantGradientBlock`](@ref) for a version where [`qval`](@ref) is variable.
 """
 struct ConcreteInstantGradient <: AbstractConcreteBlock
-    builder :: AbstractSequence
     orientation :: Any
     qval :: Number
 end
 
-function to_concrete_block(builder::Sequence, block::InstantGradientBlock)
-    return ConcreteInstantGradient(builder, block.orientation, value(qval(block)))
+function to_concrete_block(block::InstantGradientBlock)
+    return ConcreteInstantGradient(block.orientation, value(qval(block)))
 end
 
 
diff --git a/src/gradients/integrate_gradients.jl b/src/gradients/integrate_gradients.jl
index 9083c050c61c4d3932342a5eb7f2e230d2e1741c..8b28be490b51009ee4bad341b77ea394a8b5024f 100644
--- a/src/gradients/integrate_gradients.jl
+++ b/src/gradients/integrate_gradients.jl
@@ -1,11 +1,11 @@
 module IntegrateGradients
+import ...Variables: qval, bval
 import ...BuildingBlocks: BuildingBlock
-import ...SequenceBuilders: SequenceBuilder, TR, duration, builder, index
+import ...Containers: Sequence
 
 
 """
-    qval(blocks)
-    qval(builder::SequenceBuilder, indices)
+    qval(container, indices)
 
 Computes the total q-value summed over multiple gradient [`BuildingBlock`](@ref) objects.
 
@@ -19,12 +19,10 @@ In addition, in this interface one can provide negative indices to indicate that
 
 The integral can occur over multiple repetition times by including [`BuildingBlock`](@ref) objects out of order (or by using :TR).
 """
-qval(builder::SequenceBuilder, indices::AbstractVector) = full_integral(builder, indices)[1]
-qval(indices::AbstractVector) = full_integral(indices)[1]
+qval(builder::Sequence, indices::AbstractVector) = full_integral(builder, indices)[1]
 
 """
-    bval(blocks)
-    bval(builder::SequenceBuilder, indices)
+    bval(container, indices)
 
 Computes the total b-value combined over multiple gradient [`BuildingBlock`](@ref) objects.
 
@@ -38,20 +36,9 @@ In addition, in this interface one can provide negative indices to indicate that
 
 The integral can occur over multiple repetition times by including [`BuildingBlock`](@ref) objects out of order (or by using :TR).
 """
-bval(builder::SequenceBuilder, indices::AbstractVector) = full_integral(builder, indices)[2]
-bval(indices::AbstractVector) = full_integral(indices)[2]
+bval(builder::Sequence, indices::AbstractVector) = full_integral(builder, indices)[2]
 
-function full_integral(blocks::AbstractVector)
-    actual_blocks = filter(b -> b isa BuildingBlock)
-    if length(actual_blocks) == 0
-        return (0., 0.)
-    end
-    sb = builder(blocks[1])
-
-    return full_integral(sb, map(b -> b isa BuildingBlock ? index(sb, b) : b))
-end
-
-function full_integral(builder::SequenceBuilder, indices::AbstractVector)
+function full_integral(builder::Sequence, indices::AbstractVector)
     qval_current = 0.
     current_index = 0
     bval_current = 0.
diff --git a/src/gradients/pulsed_gradients.jl b/src/gradients/pulsed_gradients.jl
index a140346656a3f6f032736b1c8f13bbcc934a5e75..6a91760c355235c899d44deda07fd50f25385ac5 100644
--- a/src/gradients/pulsed_gradients.jl
+++ b/src/gradients/pulsed_gradients.jl
@@ -5,10 +5,10 @@ module PulsedGradients
 
 import JuMP: @constraint, @variable, Model, VariableRef, owner_model, value
 import StaticArrays: SVector
-import ...BuildingBlocks: BuildingBlock, duration, properties, set_simple_constraints!, BuildingBlockPlaceholder, gradient_strength, slew_rate
-import ...SequenceBuilders: SequenceBuilder, start_time
-import ...ConcreteBlocks: ConcreteBlock, to_concrete_block, AbstractSequence
-import ..IntegrateGradients: qval, bval
+import ...Variables: qval, bval, rise_time, flat_time, slew_rate, gradient_strength, variables, duration, δ, get_free_variable, VariableType
+import ...BuildingBlocks: BuildingBlock, duration, set_simple_constraints!
+import ...ConcreteBlocks: ConcreteBlock, to_concrete_block
+import ...GlobalModel: @global_model_constructor
 
 
 """
@@ -36,66 +36,36 @@ If not set, they will be determined during the sequence optimisation.
 The [`bvalue`](@ref) can be constrained for multiple gradient pulses.
 """
 mutable struct PulsedGradient <: BuildingBlock
-    builder::SequenceBuilder
+    model :: Model
     orientation :: Any
-    slew_rate :: VariableRef
-    rise_time :: VariableRef
-    flat_time :: VariableRef
+    slew_rate :: VariableType
+    rise_time :: VariableType
+    flat_time :: VariableType
 end
 
-function PulsedGradient(; kwargs...)
-    return BuildingBlockPlaceholder{PulsedGradient}(; kwargs...)
-end
+@global_model_constructor PulsedGradient
 
-function PulsedGradient(builder::SequenceBuilder; orientation=:bvec, kwargs...)
+function PulsedGradient(model::Model; orientation=:bvec, slew_rate=nothing, rise_time=nothing, flat_time=nothing, kwargs...)
     model = owner_model(builder)
     res = PulsedGradient(
         builder,
         orientation,
-        @variable(model),
-        @variable(model),
-        @variable(model)
+        [get_free_variable(model, value) for value in (slew_rate, rise_time, flat_time)]...
     )
 
     set_simple_constraints!(model, res, kwargs)
-    @constraint model flat_time(res) >= 0
-    @constraint model rise_time(res) >= 0
-    @constraint model slew_rate(res) >= 0
+    @constraint model res.flat_time >= 0
+    @constraint model res.rise_time >= 0
+    @constraint model res.slew_rate >= 0
     return res
 end
 
-"""
-    rise_time(pulsed_gradient)
-
-The time from 0 till the maximum gradient strength in ms.
-"""
 rise_time(pg::PulsedGradient) = pg.rise_time
-
-"""
-    flat_time(pulsed_gradient)
-
-The time spent at the maximum gradient strength in ms.
-"""
 flat_time(pg::PulsedGradient) = pg.flat_time
-
 gradient_strength(g::PulsedGradient) = rise_time(g) * slew_rate(g)
-
 slew_rate(g::PulsedGradient) = g.slew_rate
-
-"""
-    δ(pulsed_gradient)
-
-Pulse gradient duration (`rise_time + `flat_time`).  This is the effective duration of the gradient. The real duration is longer (and given by [`duration`](@ref)).
-"""
 δ(g::PulsedGradient) = rise_time(g) + flat_time(g)
-
 duration(g::PulsedGradient) = 2 * rise_time(g) + flat_time(g)
-
-"""
-    qval(gradient)
-
-q-value at the end of the gradient (rad/um).
-"""
 qval(g::PulsedGradient) = (g.orientation == :neg_bvec ? -1 : 1) * gradient_strength(g) * δ(g)
 
 
@@ -116,7 +86,7 @@ end
 variables(::Type{<:PulsedGradient}) = [qval, δ, gradient_strength, duration, rise_time, flat_time, slew_rate]
 
 
-function to_concrete_block(s::AbstractSequence, block::PulsedGradient)
+function to_concrete_block(block::PulsedGradient)
     if block.orientation == :bvec
         rotate = true
         qvec = [value(qval(block)), 0., 0.]
@@ -131,7 +101,7 @@ function to_concrete_block(s::AbstractSequence, block::PulsedGradient)
     end
     t_rise = value(rise_time(block))
     t_d = value(δ(block))
-    return ConcreteBlock(s, t_d + t_rise, gradient=[
+    return ConcreteBlock(t_d + t_rise, gradient=[
         (0., zeros(3)),
         (t_rise, qvec),
         (t_d, qvec),
diff --git a/src/inserted_building_blocks.jl b/src/inserted_building_blocks.jl
new file mode 100644
index 0000000000000000000000000000000000000000..4a4b54db309d47141f2303e23e45bd25c6dc1e71
--- /dev/null
+++ b/src/inserted_building_blocks.jl
@@ -0,0 +1,64 @@
+module InsertedBuildingBlocks
+import Printf: @sprintf
+import JuMP: owner_model, has_values, Model
+import ..GlobalModel: @global_model_constructor
+import ..Variables: Variables, duration, start_time, variables, end_time, get_free_variable
+import ..BuildingBlocks: BuildingBlock, internal_print
+
+"""
+    InsertedBuildingBlock(building_block; start_time=nothing)
+    InsertedBuildingBlock(building_block, index)
+
+Represents a specific insertion of the [`BuildingBlock`](@ref) object within a larger sequence.
+
+If `index` is set an existing [`InsertedBuildingBlock`](@ref) is returned. Otherwise, a new one is created with the given `start_time` as constraint.
+"""
+struct InsertedBuildingBlock{T<:BuildingBlock}
+    bb :: T
+    index :: Int
+    function InsertedBuildingBlock(bb, index)
+        if index < 1 || index > length(bb)
+            error("$index is out of range for $bb")
+        end
+        return new{typeof(bb)}(bb, index)
+    end
+end
+
+owner_model(inserted::InsertedBuildingBlock) = owner_model(inserted.bb)
+has_values(inserted::InsertedBuildingBlock) = has_values(owner_model(inserted))
+
+@global_model_constructor InsertedBuildingBlock
+
+function InsertedBuildingBlock(model::Model, bb::BuildingBlock; start_time=nothing)
+    index = length(bb.start_time) + 1
+    push!(bb.start_time, get_free_variable(model, start_time))
+    InsertedBuildingBlock(bb, index)
+end
+
+
+function Base.show(io::IO, inserted::InsertedBuildingBlock)
+    print(io, string(typeof(block)), "(")
+    if has_values(inserted)
+        if iszero(value(duration(block)))
+            print(io, "time=", @sprintf("%.3f", value(start_time(block))), ", ")
+        else
+            print(
+                io, "time=", 
+                @sprintf("%.3f", value(start_time(block))), "-",
+                @sprintf("%.3f", value(end_time(block))), ", "
+            )
+        end
+    end
+    internal_print(io, inserted.bb)
+    print(io, ")")
+end
+
+for func in variables()
+    if func in (start_time, end_time)
+        continue
+    end
+    @eval Variables.$(nameof(func))(inserted::InsertedBuildingBlock, args...; kwargs...) = Variables.$(nameof(func))(inserted.bb, args...; kwargs...)
+end
+
+
+end
\ No newline at end of file
diff --git a/src/pulses/constant_pulses.jl b/src/pulses/constant_pulses.jl
index c0e174d5e0bec71e10ffd58f26ee19a2f8585bf2..c83209a1dc800a8e517eb95621492e4b81bfed68 100644
--- a/src/pulses/constant_pulses.jl
+++ b/src/pulses/constant_pulses.jl
@@ -1,9 +1,9 @@
 module ConstantPulses
-import JuMP: VariableRef, @constraint, @variable, value
-import ...BuildingBlocks: BuildingBlock, properties, BuildingBlockPlaceholder, set_simple_constraints!, duration
-import ...SequenceBuilders: SequenceBuilder, owner_model, start_time, end_time, AbstractSequence
+import JuMP: VariableRef, @constraint, @variable, value, Model
+import ...BuildingBlocks: BuildingBlock, set_simple_constraints!
 import ...ConcreteBlocks: ConcreteBlock, to_concrete_block
-import ..Properties: flip_angle, phase, amplitude, frequency, bandwidth
+import ...Variables: variables, get_free_variable, flip_angle, phase, amplitude, frequency, bandwidth, start_time, end_time, VariableType, duration
+import ...GlobalModel: @global_model_constructor
 
 """
     ConstantPulse(; variables...)
@@ -18,24 +18,21 @@ Represents an radio-frequency pulse with a constant amplitude and frequency (i.e
 - [`frequency`](@ref): frequency of the RF pulse relative to the Larmor frequency (in kHz).
 """
 struct ConstantPulse <: BuildingBlock
-    builder :: SequenceBuilder
-    amplitude :: VariableRef
-    duration :: VariableRef
-    phase :: VariableRef
-    frequency :: VariableRef
+    model :: Model
+    amplitude :: VariableType
+    duration :: VariableType
+    phase :: VariableType
+    frequency :: VariableType
 end
 
-ConstantPulse(; kwargs...) = BuildingBlockPlaceholder{ConstantPulse}(; kwargs...)
-function ConstantPulse(builder::SequenceBuilder; kwargs...) 
-    model = owner_model(builder)
+@global_model_constructor ConstantPulse
+
+function ConstantPulse(model::Model; amplitude=nothing, duration=nothing, phase=nothing, frequency=nothing, kwargs...) 
     res = ConstantPulse(
-        builder,
-        @variable(model),
-        @variable(model),
-        @variable(model),
-        @variable(model)
+        model,
+        [get_free_variable(model, value) for value in (amplitude, duration, phase, frequency)]...
     )
-    @constraint model amplitude(res) >= 0
+    @constraint model res.amplitude >= 0
     set_simple_constraints!(model, res, kwargs)
     return res
 end
@@ -49,10 +46,10 @@ bandwidth(pulse::ConstantPulse) = 3.79098854 / duration(pulse)
 
 variables(::Type{<:ConstantPulse}) = [amplitude, duration, phase, frequency, flip_angle, bandwidth]
 
-function to_concrete_block(s::AbstractSequence, block::ConstantPulse)
+function to_concrete_block(block::ConstantPulse)
     d = value(duration(block))
     final_phase = value(phase(block)) + d * value(frequency(block)) * 360
-    return ConcreteBlock(s, value(duration(block)), pulse=[
+    return ConcreteBlock(value(duration(block)), pulse=[
         ([0., d]),
         value.([amplitude(block), amplitude(block)]),
         value.([phase(block), final_phase])
diff --git a/src/pulses/instant_pulses.jl b/src/pulses/instant_pulses.jl
index 863fbfe04109cdd412c42da371d0aa218262a749..ffee58be9710c8c189ef20f37ca7030a21c0dff6 100644
--- a/src/pulses/instant_pulses.jl
+++ b/src/pulses/instant_pulses.jl
@@ -1,25 +1,25 @@
 module InstantPulses
-import JuMP: @constraint, @variable, VariableRef, value
-import ...BuildingBlocks: BuildingBlock, properties, BuildingBlockPlaceholder, set_simple_constraints!, duration
-import ...SequenceBuilders: SequenceBuilder, owner_model, start_time
-import ...ConcreteBlocks: to_concrete_block, AbstractConcreteBlock, Sequence, AbstractSequence
-import ..Properties: flip_angle, phase
+import JuMP: @constraint, @variable, VariableRef, value, Model
+import ...BuildingBlocks: BuildingBlock
+import ...ConcreteBlocks: to_concrete_block, AbstractConcreteBlock
+import ...Variables: flip_angle, phase, start_time, variables, duration, get_free_variable, VariableType
+import ...GlobalModel: @global_model_constructor
 
 struct InstantRFPulseBlock <: BuildingBlock
-    builder :: SequenceBuilder
-    flip_angle :: VariableRef
-    phase :: VariableRef
+    model :: Model
+    flip_angle :: VariableType
+    phase :: VariableType
 end
 
-InstantRFPulseBlock(; kwargs...) = BuildingBlockPlaceholder{InstantRFPulseBlock}(; kwargs...)
-function InstantRFPulseBlock(builder::SequenceBuilder; kwargs...) 
-    model = owner_model(builder)
+@global_model_constructor InstantRFPulseBlock
+
+function InstantRFPulseBlock(model::Model; flip_angle=nothing, phase=nothing) 
     res = InstantRFPulseBlock(
-        builder,
-        @variable(model),
-        @variable(model)
+        model,
+        get_free_variable(model, flip_angle),
+        get_free_variable(model, phase)
     )
-    @constraint model flip_angle(res) >= 0
+    @constraint model res.flip_angle >= 0
     set_simple_constraints!(model, res, kwargs)
     return res
 end
@@ -38,13 +38,12 @@ Instantaneous RF pulse with no free variables.
 See [`InstantRFPulseBlock`](@ref) for a version where [`flip_angle`](@ref) and [`phase`](@ref) are variables.
 """
 struct ConcreteInstantRFPulse <: AbstractConcreteBlock
-    builder :: AbstractSequence
     flip_angle :: Number
     phase :: Number
 end
 
-function to_concrete_block(builder::Sequence, block::InstantRFPulseBlock)
-    return ConcreteInstantRFPulse(builder, value(flip_angle(block)), value(phase(block)))
+function to_concrete_block(block::InstantRFPulseBlock)
+    return ConcreteInstantRFPulse(value(flip_angle(block)), value(phase(block)))
 end
 
 end
\ No newline at end of file
diff --git a/src/pulses/properties.jl b/src/pulses/properties.jl
deleted file mode 100644
index abde613511dfb62df9450f4f80f0a34d0d88b2a4..0000000000000000000000000000000000000000
--- a/src/pulses/properties.jl
+++ /dev/null
@@ -1,39 +0,0 @@
-module Properties
-
-"""
-    flip_angle(pulse_block)
-
-The flip angle of the RF pulse in a [`BuildingBlock`](@ref) in degrees.
-"""
-function flip_angle end
-
-"""
-    phase(pulse_block)
-
-The angle of the phase at the start of the RF pulse in a [`BuildingBlock`](@ref) in degrees.
-"""
-function phase end
-
-"""
-    amplitude(pulse_block)
-
-The maximum amplitude during the RF pulse in a [`BuildingBlock`](@ref) in kHz.
-"""
-function amplitude end
-
-"""
-    frequency(pulse_block)
-
-The maximum frequency during the RF pulse in a [`BuildingBlock`](@ref) relative to the Larmor frequency in kHz.
-"""
-function frequency end
-
-
-"""
-    bandwidth(pulse_block)
-
-FWHM of the frequency content of the RF pulse in kHz.
-"""
-function bandwidth end
-
-end
\ No newline at end of file
diff --git a/src/pulses/pulses.jl b/src/pulses/pulses.jl
index fd9b9862c34f32c9ff6509db662fc904db7770b9..83354e61977277fe49ab1f2504bb21a092f5ebce 100644
--- a/src/pulses/pulses.jl
+++ b/src/pulses/pulses.jl
@@ -1,12 +1,10 @@
 module Pulses
-include("properties.jl")
 include("instant_pulses.jl")
 include("constant_pulses.jl")
 include("sinc_pulses.jl")
 
-import .Properties: flip_angle, phase, amplitude, frequency, bandwidth
 import .InstantPulses: InstantRFPulseBlock
 import .ConstantPulses: ConstantPulse
-import .SincPulses: SincPulse, N_left, N_right
+import .SincPulses: SincPulse
 
 end
\ No newline at end of file
diff --git a/src/pulses/sinc_pulses.jl b/src/pulses/sinc_pulses.jl
index 8320c91855be8a4e593122512d264148007bf53a..fd6e7472678e76a42c3514dd3230967d3c941a03 100644
--- a/src/pulses/sinc_pulses.jl
+++ b/src/pulses/sinc_pulses.jl
@@ -1,12 +1,12 @@
 module SincPulses
 
-import JuMP: VariableRef, @constraint, @variable, value
+import JuMP: VariableRef, @constraint, @variable, value, Model
 import QuadGK: quadgk
 import Polynomials: fit, Polynomial
-import ...BuildingBlocks: BuildingBlock, properties, BuildingBlockPlaceholder, set_simple_constraints!, duration
-import ...SequenceBuilders: SequenceBuilder, owner_model, start_time, end_time, AbstractSequence
+import ...BuildingBlocks: BuildingBlock, set_simple_constraints!
 import ...ConcreteBlocks: ConcreteBlock, to_concrete_block
-import ..Properties: flip_angle, phase, amplitude, frequency, bandwidth
+import ...Variables: flip_angle, phase, amplitude, frequency, bandwidth, VariableType, variables, get_free_variable, duration
+import ...GlobalModel: @global_model_constructor
 
 """
     SincPulse(; symmetric=true, max_Nlobes=nothing, apodise=true, variables...)
@@ -29,27 +29,32 @@ Represents an radio-frequency pulse with a constant amplitude and frequency.
 - [`bandwidth`](@ref): width of the rectangular function in frequency space (in kHz). If the `duration` is short (compared with 1/`bandwidth`), this bandwidth will only be approximate.
 """
 struct SincPulse <: BuildingBlock
-    builder :: SequenceBuilder
+    model :: Model
     symmetric :: Bool
     apodise :: Bool
     nlobe_integral :: Polynomial
-    N_left :: VariableRef
-    N_right :: VariableRef
-    amplitude :: VariableRef
-    phase :: VariableRef
-    frequency :: VariableRef
-    lobe_duration :: VariableRef
+    N_left :: VariableType
+    N_right :: VariableType
+    amplitude :: VariableType
+    phase :: VariableType
+    frequency :: VariableType
+    lobe_duration :: VariableType
 end
 
-SincPulse(; kwargs...) = BuildingBlockPlaceholder{SincPulse}(; kwargs...)
-function SincPulse(builder::SequenceBuilder; symmetric=true, max_Nlobes=nothing, apodise=true, kwargs...) 
-    model = owner_model(builder)
+@global_model_constructor SincPulse
+
+function SincPulse(model::Model; 
+    symmetric=true, max_Nlobes=nothing, apodise=true, N_lobes=nothing, N_left=nothing, N_right=nothing, 
+    amplitude=nothing, phase=nothing, frequency=nothing, lobe_duration=nothing, kwargs...
+) 
     if symmetric
-        N_lobes = @variable(model, integer=true)
+        N_lobes = get_free_variable(model, N_lobes)
+        @assert isnothing(N_left) && isnothing(N_right) "N_left and N_right cannot be set if symmetric=true (default)"
         N_left_var = N_right_var = N_lobes
     else
-        N_left_var = @variable(model, integer=true)
-        N_right_var = @variable(model, integer=true)
+        @assert isnothing(N_lobes) "N_lobes cannot be set if symmetric=true (default)"
+        N_left_var = get_free_variable(model, N_left)
+        N_right_var = get_free_variable(model, N_right)
     end
     res = SincPulse(
         builder,
@@ -58,15 +63,12 @@ function SincPulse(builder::SequenceBuilder; symmetric=true, max_Nlobes=nothing,
         nlobe_integral_params(max_Nlobes, apodise),
         N_left_var,
         N_right_var,
-        @variable(model),
-        @variable(model),
-        @variable(model),
-        @variable(model)
+        [get_free_variable(model, value) for value in (amplitude, phase, frequeny, lobe_duration)]...
     )
-    @constraint model amplitude(res) >= 0
-    @constraint model N_left(res) >= 1
+    @constraint model res.amplitude >= 0
+    @constraint model res.N_left >= 1
     if !symmetric
-        @constraint model N_right(res) >= 1
+        @constraint model res.N_right >= 1
     end
     set_simple_constraints!(model, res, kwargs)
     return res
@@ -100,13 +102,13 @@ lobe_duration(pulse::SincPulse) = pulse.lobe_duration
 bandwidth(pulse::SincPulse) = 1 / lobe_duration(pulse)
 variables(::Type{<:SincPulse}) = [amplitude, N_left, N_right, duration, phase, frequency, flip_angle, lobe_duration, bandwidth]
 
-function to_concrete_block(s::AbstractSequence, block::SincPulse)
+function to_concrete_block(block::SincPulse)
     normed_times = -value(N_left(block)):0.1:value(N_right(block)) + 1e-5
     times = ((normed_times .+ value(N_left(block))) .* value(lobe_duration(block)))
     amplitudes = value(amplitude(block)) .* (normalised_function.(normed_times; apodise=block.apodise))
     phases = (value(frequency(block)) .* value(lobe_duration(block))) .* normed_times * 360
     return ConcreteBlock(
-        s, value(duration(block)); 
+        value(duration(block)); 
         pulse=(times, amplitudes, phases)
     )
 end
diff --git a/src/readouts/instant_readouts.jl b/src/readouts/instant_readouts.jl
index 645809ba6d9c746eaa25c280d22604b479eb7816..6f3bf4cd5da847b7935aea7a6e0301d4f7a0300b 100644
--- a/src/readouts/instant_readouts.jl
+++ b/src/readouts/instant_readouts.jl
@@ -1,7 +1,7 @@
 module InstantReadouts
-import ...BuildingBlocks: BuildingBlock, BuildingBlockPlaceholder, duration, properties
-import ...SequenceBuilders: SequenceBuilder, start_time, to_block, AbstractSequence
+import ...BuildingBlocks: BuildingBlock
 import ...ConcreteBlocks: AbstractConcreteBlock, to_concrete_block
+import ...Variables: variables
 
 """
     InstantReadout()
@@ -11,13 +11,8 @@ Represents an instantaneous `Readout` of the signal.
 It has no parameters or properties to set.
 """
 struct InstantReadout <: AbstractConcreteBlock
-    builder::AbstractSequence
 end
 
-InstantReadout() = BuildingBlockPlaceholder{InstantReadout}()
-
 variables(::Type{<:InstantReadout}) = []
-to_block(builder::SequenceBuilder, cls::Type{<:InstantReadout}) = cls(builder)
-to_concrete_block(builder::AbstractSequence, ::InstantReadout) = InstantReadout(builder)
-
+to_concrete_block(::InstantReadout) = InstantReadout()
 end
\ No newline at end of file
diff --git a/src/variables.jl b/src/variables.jl
new file mode 100644
index 0000000000000000000000000000000000000000..bff48c7d4c369b798bacd83ac00505c30ceb1921
--- /dev/null
+++ b/src/variables.jl
@@ -0,0 +1,92 @@
+module Variables
+import JuMP: @variable, Model, @objective, objective_function, owner_model, has_values, value, AbstractJuMPScalar
+import ..Scanners: gradient_strength, slew_rate
+
+all_variables_symbols = [
+    # general
+    :duration => (:block, "duration of the building block in ms."),
+
+    # RF pulse
+    :flip_angle => (:pulse, "The flip angle of the RF pulse in degrees"),
+    :amplitude => (:pulse, "The maximum amplitude of an RF pulse in kHz"),
+    :phase => (:pulse, "The angle of the phase of an RF pulse in KHz"),
+    :frequency => (:pulse, "The off-resonance frequency of an RF pulse (relative to the Larmor frequency of water) in KHz"),
+    :bandwidth => (:pulse, "Bandwidth of the RF pulse in kHz"),
+    :N_left => (:pulse, "The number of zero crossings of the RF pulse before the main peak"),
+    :N_right => (:pulse, "The number of zero crossings of the RF pulse after the main peak"),
+
+    # gradients
+    :qval => (:gradient, "The spatial range on which the displacements can be detected due to this gradient in 1/um (equivalent to [`area_under_curve`](@ref))"),
+    :area_under_curve => (:gradient, "Area under the curve of the gradient in 1/um (equivalent to [`qval`](@ref))."),
+    :δ => (:gradient, "Effective duration of a gradient pulse ([`rise_time`](@ref) + [`flat_time`](@ref)) in ms."),
+    :rise_time => (:gradient, "Time for gradient pulse to reach its maximum value in ms."),
+    :flat_time => (:gradient, "Time of gradient pulse at maximum value in ms."),
+    :slew_rate => (:gradient, "maximum increase of a gradient (kHz/um/ms)"),
+    :gradient_strength => (:gradient, "maximum strength of a gradient (kHz/um)"),
+]
+
+symbol_to_func = Dict{Symbol, Function}()
+
+
+
+for (func_symbol, (block_symbol, description)) in all_variables_symbols
+    as_string = "    $func_symbol($block_symbol)\n\n$description\n\nThis represents a variable within the sequence. Variables can be set during the construction of a [`BuildingBlock`](@ref) or used to create constraints after the fact."
+    new_func = @eval begin
+        $as_string
+        function $func_symbol end
+    end
+    symbol_to_func[func_symbol] = new_func
+end
+
+
+"""
+    variables(building_block)
+    variables()
+
+Returns all functions representing properties of a [`BuildingBlock`](@ref) object.
+"""
+variables() = [values(symbol_to_func)...]
+
+
+# Some universal truths
+area_under_curve(bb) = qval(bb)
+
+
+function start_time end
+function end_time end
+
+
+const VariableType = Union{Number, AbstractJuMPScalar}
+
+
+"""
+    get_free_variable(model, value; integer=false)
+
+Get a representation of a given `variable` given a user-defined constraint.
+"""
+get_free_variable(::Model, value::Number; integer=false) = integer ? Int(value) : Float64(value)
+function get_free_variable(model::Model, value::VariableType; integer=false)
+    if owner_model(value) != model
+        if has_values(value)
+            return value(value)
+        end
+        error("Cannot set any constraints between sequences stored in different JuMP models.")
+    end
+    return value
+end
+get_free_variable(model::Model, ::Nothing; integer=false) = @variable(model, integer=integer)
+get_free_variable(model::Model, value::Symbol; integer=false) = integer ? error("Cannot maximise or minimise an integer variable") : get_free_variable(model, Val(value))
+function get_free_variable(model::Model, ::Val{:min})
+    var = get_free_variable(model, nothing)
+    @objective model Min objective_function(model) + var
+    return var
+end
+function get_free_variable(model::Model, ::Val{:max})
+    var = get_free_variable(model, nothing)
+    @objective model Min objective_function(model) - var
+    return var
+end
+
+function bval end
+
+end
\ No newline at end of file
diff --git a/src/wait.jl b/src/wait.jl
index 8888903aebb84792b76016bb0b2ed0db12a0c3fb..9bf981af88d237c6d59160669a2b7204a8ff3e4e 100644
--- a/src/wait.jl
+++ b/src/wait.jl
@@ -1,8 +1,9 @@
 module Wait
 import JuMP: Model, @constraint, @variable, VariableRef, owner_model, value
-import ..BuildingBlocks: BuildingBlock, duration, properties, apply_simple_constraint!, BuildingBlockPlaceholder
-import ..SequenceBuilders: SequenceBuilder, to_block, AbstractSequence
+import ..Variables: VariableType, variables, duration, get_free_variable
+import ..BuildingBlocks: BuildingBlock
 import ..ConcreteBlocks: to_concrete_block, ConcreteBlock
+import ..GlobalModel: @global_model_constructor
 import ...Scanners: Scanner
 
 """
@@ -17,31 +18,29 @@ Duration can be set to one of:
 - `nothing` to make it fully determined by external constraints and objectives
 """
 struct WaitBlock <: BuildingBlock
-    builder :: SequenceBuilder
-    duration :: VariableRef
+    model :: Model
+    duration :: VariableType
 end
 
-function WaitBlock(builder::SequenceBuilder, duration_constraint=nothing)
-    model = owner_model(builder)
-    res = WaitBlock(builder, @variable(model))
+@global_model_constructor WaitBlock
+
+function WaitBlock(model::Model, duration=nothing)
+    res = WaitBlock(
+        model, 
+        get_free_variable(model, duration),
+        VariableType[]
+    )
     @constraint model duration(res) >= 0
-    if !isnothing(duration_constraint)
-        apply_simple_constraint!(model, duration(res), duration_constraint)
-    end
     return res
 end
 
-WaitBlock(duration_constraint=nothing) = BuildingBlockPlaceholder{WaitBlock}(duration_constraint)
-
-to_block(builder::SequenceBuilder, time::Union{Number, Symbol, Nothing, Val{:min}, Val{:max}}) = WaitBlock(builder, time)
+to_block(time::Union{VariableType, Symbol, Nothing, Val{:min}, Val{:max}}) = WaitBlock(time)
 
 
 variables(::Type{WaitBlock}) = [duration]
 
 duration(wb::WaitBlock) = wb.duration
 
-scanner_constraints!(::Model, ::WaitBlock, ::Scanner) = nothing
-
-to_concrete_block(builder::AbstractSequence, wb::WaitBlock) = ConcreteBlock(builder, value(duration(wb)))
+to_concrete_block(wb::WaitBlock) = ConcreteBlock(value(duration(wb)))
 
 end
\ No newline at end of file