break and discard

The break, continue and discard Statements

break leaves the innermost loop or switch and continue skips to the next iteration (through the continuing block, if any). This shopping loop skips the out-of-stock book and stops at a US$60 budget:

continue skips a book, break ends the loopJavaScript
// out: 2 x f32
@group(0) @binding(0) var<storage, read_write> out: array<f32>;
const prices = array(14.99, 39.50, 24.00, 18.75, 16.20, 21.30);
const inStock = array(true, true, false, true, true, true);
@compute @workgroup_size(1) fn main() {
  var cart = 0.0;  var count = 0.0;
  for (var i = 0; i < 6; i++) {
    if (!inStock[i]) { continue; }
    if (cart + prices[i] > 60) { break; }
    cart += prices[i]; count += 1;
  }
  out[0] = cart; out[1] = count;
}
Output
out: 54.48999786376953, 2

A continuing block may not contain continue ("continuing blocks must not contain a continue statement"). discard exists only in fragment shaders ("discard statement cannot be used in compute pipeline stage"): it throws the fragment away, writing no color or depth, which cuts shapes out of quads (First Shader Pair). The fragment becomes a helper invocation that may keep running so its neighbors' derivatives stay valid.

continue skips a sold-out book and break stops at a $60 budget; discard cuts bookmark notches out of the coversHTMLLive
<!doctype html>
<style>
  body { margin: 0; background: #f7f4ee; font: 14px system-ui, sans-serif; }
  .stage { position: relative; width: 100%; max-width: 600px; }
  .stage canvas { display: block; width: 100%; }
  .stage canvas + canvas { position: absolute; inset: 0; pointer-events: none; }
</style>
<div class="stage">
  <canvas id="view" width="600" height="340"></canvas>
  <canvas id="labels" width="600" height="340"></canvas>
</div>
<script>
const canvas = document.getElementById('view');
const ink = document.getElementById('labels').getContext('2d');

function showMessage(text) {                     // 2D fallback when WebGPU is missing
  const ctx = canvas.getContext('2d');
  ctx.fillStyle = '#fbeaea'; ctx.fillRect(0, 0, canvas.width, canvas.height);
  ctx.fillStyle = '#8a2b2b'; ctx.font = '18px system-ui, sans-serif'; ctx.textAlign = 'center';
  ctx.fillText(text, canvas.width / 2, canvas.height / 2);
}
function label(text, x, y, size = 12, color = '#2b2b2b', align = 'left', weight = '') {
  ink.font = `${weight} ${size}px system-ui, sans-serif`; ink.fillStyle = color; ink.textAlign = align; ink.fillText(text, x, y);
}

// The shopping loop; out[2 + i] records what happened to each book for the picture.
const lab = /* wgsl */ `
@group(0) @binding(0) var<storage, read_write> out: array<f32>;
const prices = array(14.99, 39.50, 24.00, 18.75, 16.20, 21.30);
const inStock = array(true, true, false, true, true, true);
@compute @workgroup_size(1) fn main() {
  var cart = 0.0;  var count = 0.0;
  for (var i = 0; i < 6; i++) {
    if (!inStock[i]) { out[2 + i] = 1; continue; }        // 1: skipped
    if (cart + prices[i] > 60) { out[2 + i] = 2; break; } // 2: over budget, loop ends
    cart += prices[i]; count += 1;
    out[2 + i] = 3;                                        // 3: in the cart
  }
  out[0] = cart; out[1] = count;
}`;
// Covers coloured by that status; discard cuts a V-shaped notch into each cover's bottom.
const view = /* wgsl */ `
@group(0) @binding(0) var<storage> out: array<f32>;
struct Out { @builtin(position) pos: vec4f, @location(0) uv: vec2f, @location(1) @interpolate(flat) book: u32 }
@vertex fn vs(@builtin(vertex_index) v: u32, @builtin(instance_index) i: u32) -> Out {
  let q = vec2f(f32(v & 1), f32(v >> 1));
  let px = vec2f(24 + f32(i) * 96 + q.x * 72, 200 - q.y * 130);
  return Out(vec4f(px.x / 300 - 1, 1 - px.y / 170, 0, 1), q, i);
}
@fragment fn fs(in: Out) -> @location(0) vec4f {
  if (in.uv.y < 0.18 - abs(in.uv.x - 0.5) * 0.36) { discard; }   // no colour, no depth written
  let status = u32(out[2 + in.book]);
  let colors = array(vec3f(0.80, 0.79, 0.76), vec3f(0.80, 0.79, 0.76), vec3f(0.85, 0.35, 0.25), vec3f(0.16, 0.56, 0.30));
  let band = step(0.7, in.uv.y) * step(in.uv.y, 0.78);
  return vec4f(mix(colors[status], vec3f(1), band * 0.6), 1);
}`;
const prices = [14.99, 39.50, 24.00, 18.75, 16.20, 21.30];
const titles = ['Quiet Harbor', 'Small Steps', 'Salt and Saffron', "Clockmaker's", 'Night Trains', 'Paper Gardens'];

async function main() {
  const adapter = await navigator.gpu?.requestAdapter();
  if (!adapter) return showMessage('WebGPU is not available in this browser');
  const device = await adapter.requestDevice();
  const context = canvas.getContext('webgpu');
  const format = navigator.gpu.getPreferredCanvasFormat();
  context.configure({ device, format });
  const B = GPUBufferUsage;
  const out = device.createBuffer({ size: 32, usage: B.STORAGE | B.COPY_SRC });
  const read = device.createBuffer({ size: 32, usage: B.COPY_DST | B.MAP_READ });
  const compute = device.createComputePipeline({ layout: 'auto', compute: { module: device.createShaderModule({ code: lab }) } });
  const module = device.createShaderModule({ code: view });
  const render = device.createRenderPipeline({ layout: 'auto', primitive: { topology: 'triangle-strip' },
    vertex: { module }, fragment: { module, targets: [{ format }] } });

  const encoder = device.createCommandEncoder();
  const cp = encoder.beginComputePass();
  cp.setPipeline(compute);
  cp.setBindGroup(0, device.createBindGroup({ layout: compute.getBindGroupLayout(0), entries: [{ binding: 0, resource: { buffer: out } }] }));
  cp.dispatchWorkgroups(1);
  cp.end();
  const pass = encoder.beginRenderPass({ colorAttachments: [{ view: context.getCurrentTexture().createView(),
    clearValue: [0.97, 0.96, 0.93, 1], loadOp: 'clear', storeOp: 'store' }] });
  pass.setPipeline(render);
  pass.setBindGroup(0, device.createBindGroup({ layout: render.getBindGroupLayout(0), entries: [{ binding: 0, resource: { buffer: out } }] }));
  pass.draw(4, 6);
  pass.end();
  encoder.copyBufferToBuffer(out, 0, read, 0, 32);
  device.queue.submit([encoder.finish()]);
  await read.mapAsync(GPUMapMode.READ);
  const o = new Float32Array(read.getMappedRange().slice(0));
  read.unmap();

  label('Shopping with a $60 budget', 24, 36, 14, '#222', 'left', 'bold');
  const words = ['not reached', 'continue: sold out', 'break: over budget', 'in the cart'];
  titles.forEach((t, i) => {
    label(t, 60 + i * 96, 222, 11, '#222', 'center');
    label(`$${prices[i].toFixed(2)}`, 60 + i * 96, 238, 11, '#555', 'center');
    label(words[o[2 + i]], 60 + i * 96, 256, 10.5, ['#999', '#777', '#b03a28', '#1e6b3a'][o[2 + i]], 'center');
  });
  label(`cart = $${o[0].toFixed(2)}, count = ${o[1]}   (books after the break are never examined)`, 24, 296, 12.5, '#1f4f8a');
  label('The notches are discarded fragments: discard exists only in fragment shaders.', 24, 318, 12, '#444');
}
main();
</script>