diff --git a/packages/boxel-ui/addon/raw-icons/icon-plus-thin.svg b/packages/boxel-ui/addon/raw-icons/icon-plus-thin.svg
new file mode 100644
index 0000000000..6927a0a7c5
--- /dev/null
+++ b/packages/boxel-ui/addon/raw-icons/icon-plus-thin.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="3.5 3.5 17 17"><path d="M19,11H13V5a1,1,0,0,0-2,0v6H5a1,1,0,0,0,0,2h6v6a1,1,0,0,0,2,0V13h6a1,1,0,0,0,0-2Z" fill="var(--icon-color, currentColor)"/></svg>
\ No newline at end of file
diff --git a/packages/boxel-ui/addon/src/icons.gts b/packages/boxel-ui/addon/src/icons.gts
index 42be4880ca..5477b50954 100644
--- a/packages/boxel-ui/addon/src/icons.gts
+++ b/packages/boxel-ui/addon/src/icons.gts
@@ -40,6 +40,7 @@ import IconPencilCrossedOut from './icons/icon-pencil-crossed-out.gts';
 import IconPencilNotCrossedOut from './icons/icon-pencil-not-crossed-out.gts';
 import IconPlus from './icons/icon-plus.gts';
 import IconPlusCircle from './icons/icon-plus-circle.gts';
+import IconPlusThin from './icons/icon-plus-thin.gts';
 import IconSearch from './icons/icon-search.gts';
 import IconSearchThick from './icons/icon-search-thick.gts';
 import IconTrash from './icons/icon-trash.gts';
@@ -100,6 +101,7 @@ export const ALL_ICON_COMPONENTS = [
   IconPencilNotCrossedOut,
   IconPlus,
   IconPlusCircle,
+  IconPlusThin,
   IconSearch,
   IconSearchThick,
   IconTrash,
@@ -161,6 +163,7 @@ export {
   IconPencilNotCrossedOut,
   IconPlus,
   IconPlusCircle,
+  IconPlusThin,
   IconSearch,
   IconSearchThick,
   IconTrash,
diff --git a/packages/boxel-ui/addon/src/icons/icon-plus-thin.gts b/packages/boxel-ui/addon/src/icons/icon-plus-thin.gts
new file mode 100644
index 0000000000..e11235e221
--- /dev/null
+++ b/packages/boxel-ui/addon/src/icons/icon-plus-thin.gts
@@ -0,0 +1,19 @@
+// This file is auto-generated by 'pnpm rebuild:icons'
+import type { TemplateOnlyComponent } from '@ember/component/template-only';
+
+import type { Signature } from './types.ts';
+
+const IconComponent: TemplateOnlyComponent<Signature> = <template>
+  <svg
+    xmlns='http://www.w3.org/2000/svg'
+    viewBox='3.5 3.5 17 17'
+    ...attributes
+  ><path
+      fill='var(--icon-color, currentColor)'
+      d='M19 11h-6V5a1 1 0 0 0-2 0v6H5a1 1 0 0 0 0 2h6v6a1 1 0 0 0 2 0v-6h6a1 1 0 0 0 0-2Z'
+    /></svg>
+</template>;
+
+// @ts-expect-error this is the only way to set a name on a Template Only Component currently
+IconComponent.name = 'IconPlusThin';
+export default IconComponent;
diff --git a/packages/host/app/components/operator-mode/code-submode/playground-panel.gts b/packages/host/app/components/operator-mode/code-submode/playground-panel.gts
index f9424437d2..be5cdf6687 100644
--- a/packages/host/app/components/operator-mode/code-submode/playground-panel.gts
+++ b/packages/host/app/components/operator-mode/code-submode/playground-panel.gts
@@ -4,9 +4,10 @@ import { action } from '@ember/object';
 import type Owner from '@ember/owner';
 import { service } from '@ember/service';
 import Component from '@glimmer/component';
+import { tracked } from '@glimmer/tracking';
 
 import Folder from '@cardstack/boxel-icons/folder';
-import { restartableTask, task } from 'ember-concurrency';
+import { task } from 'ember-concurrency';
 import perform from 'ember-concurrency/helpers/perform';
 import window from 'ember-window-mock';
 import { TrackedObject } from 'tracked-built-ins';
@@ -18,13 +19,19 @@ import {
   CardHeader,
 } from '@cardstack/boxel-ui/components';
 import { eq, or, MenuItem } from '@cardstack/boxel-ui/helpers';
-import { Eye, IconCode, IconLink } from '@cardstack/boxel-ui/icons';
+import {
+  Eye,
+  IconCode,
+  IconLink,
+  IconPlusThin,
+} from '@cardstack/boxel-ui/icons';
 
 import {
   cardTypeDisplayName,
   cardTypeIcon,
   chooseCard,
   type Query,
+  type LooseSingleCardDocument,
   type ResolvedCodeRef,
 } from '@cardstack/runtime-common';
 
@@ -33,6 +40,7 @@ import { getCard } from '@cardstack/host/resources/card-resource';
 import { getCodeRef, type CardType } from '@cardstack/host/resources/card-type';
 import { ModuleContentsResource } from '@cardstack/host/resources/module-contents';
 
+import type CardService from '@cardstack/host/services/card-service';
 import type OperatorModeStateService from '@cardstack/host/services/operator-mode-state-service';
 import type RealmService from '@cardstack/host/services/realm';
 import type RealmServerService from '@cardstack/host/services/realm-server';
@@ -87,7 +95,7 @@ const SelectedItem: TemplateOnlyComponent<{ Args: { title?: string } }> =
     </style>
   </template>;
 
-const BeforeOptions: TemplateOnlyComponent<{ Args: {} }> = <template>
+const BeforeOptions: TemplateOnlyComponent = <template>
   <div class='before-options'>
     <span class='title'>
       Recent
@@ -109,6 +117,8 @@ const BeforeOptions: TemplateOnlyComponent<{ Args: {} }> = <template>
 interface AfterOptionsSignature {
   Args: {
     chooseCard: () => void;
+    createNew: () => void;
+    createNewIsRunning?: boolean;
   };
 }
 const AfterOptions: TemplateOnlyComponent<AfterOptionsSignature> = <template>
@@ -116,6 +126,14 @@ const AfterOptions: TemplateOnlyComponent<AfterOptionsSignature> = <template>
     <span class='title'>
       Action
     </span>
+    <button class='action' {{on 'click' @createNew}} data-test-create-instance>
+      {{#if @createNewIsRunning}}
+        <LoadingIndicator class='action-running' />
+      {{else}}
+        <IconPlusThin width='16px' height='16px' />
+      {{/if}}
+      New card instance
+    </button>
     <button
       class='action'
       {{on 'click' @chooseCard}}
@@ -132,7 +150,6 @@ const AfterOptions: TemplateOnlyComponent<AfterOptionsSignature> = <template>
       border-top: var(--boxel-border);
       background-color: var(--boxel-light);
       padding: var(--boxel-sp-xs);
-      margin-top: var(--boxel-sp-xxs);
       gap: var(--boxel-sp-xxs);
     }
     .title {
@@ -152,6 +169,9 @@ const AfterOptions: TemplateOnlyComponent<AfterOptionsSignature> = <template>
     .action:hover {
       background-color: var(--boxel-100);
     }
+    .action-running {
+      --boxel-loading-indicator-size: 16px;
+    }
   </style>
 </template>;
 
@@ -193,6 +213,8 @@ class PlaygroundPanelContent extends Component<PlaygroundContentSignature> {
               @afterOptionsComponent={{component
                 AfterOptions
                 chooseCard=(perform this.chooseCard)
+                createNew=(perform this.createNew)
+                createNewIsRunning=this.createNew.isRunning
               }}
               data-test-instance-chooser
               as |card|
@@ -284,9 +306,6 @@ class PlaygroundPanelContent extends Component<PlaygroundContentSignature> {
         height: var(--boxel-form-control-height);
         box-shadow: 0 5px 10px 0 rgba(0 0 0 / 40%);
       }
-      :deep(.instances-dropdown-content > .ember-power-select-options) {
-        max-height: 20rem;
-      }
       :deep(
         .boxel-select__dropdown .ember-power-select-option[aria-current='true']
       ),
@@ -355,10 +374,12 @@ class PlaygroundPanelContent extends Component<PlaygroundContentSignature> {
     </style>
   </template>
 
+  @service private declare cardService: CardService;
   @service private declare operatorModeStateService: OperatorModeStateService;
   @service private declare realm: RealmService;
   @service private declare realmServer: RealmServerService;
   @service declare recentFilesService: RecentFilesService;
+  @tracked newCardJSON: LooseSingleCardDocument | undefined;
   private playgroundSelections: Record<
     string, // moduleId
     { cardId: string; format: Format }
@@ -411,7 +432,8 @@ class PlaygroundPanelContent extends Component<PlaygroundContentSignature> {
 
   private cardResource = getCard(
     this,
-    () => this.playgroundSelections[this.args.moduleId]?.cardId,
+    () =>
+      this.newCardJSON ?? this.playgroundSelections[this.args.moduleId]?.cardId,
     { isAutoSave: () => true },
   );
 
@@ -458,6 +480,7 @@ class PlaygroundPanelContent extends Component<PlaygroundContentSignature> {
   }
 
   private persistSelections = (cardId: string, format = this.format) => {
+    this.newCardJSON = undefined;
     this.playgroundSelections[this.args.moduleId] = { cardId, format };
     window.localStorage.setItem(
       PlaygroundSelections,
@@ -477,7 +500,7 @@ class PlaygroundPanelContent extends Component<PlaygroundContentSignature> {
     this.persistSelections(this.card.id, format);
   }
 
-  private chooseCard = restartableTask(async () => {
+  private chooseCard = task(async () => {
     let chosenCard: CardDef | undefined = await chooseCard({
       filter: { type: this.args.codeRef },
     });
@@ -488,6 +511,23 @@ class PlaygroundPanelContent extends Component<PlaygroundContentSignature> {
     }
   });
 
+  // TODO: convert this to @action once we no longer need to await below
+  private createNew = task(async () => {
+    this.newCardJSON = {
+      data: {
+        meta: {
+          adoptsFrom: this.args.codeRef,
+          realmURL: this.operatorModeStateService.realmURL.href,
+        },
+      },
+    };
+    await this.cardResource.loaded; // TODO: remove await when card-resource is refactored
+    if (this.card) {
+      this.recentFilesService.addRecentFileUrl(`${this.card.id}.json`);
+      this.persistSelections(this.card.id, 'edit'); // open new instance in playground in edit format
+    }
+  });
+
   private get canEdit() {
     return (
       this.format !== 'edit' &&
diff --git a/packages/host/tests/acceptance/code-submode/playground-test.gts b/packages/host/tests/acceptance/code-submode/playground-test.gts
index 91af62a3f4..7b311a8e9b 100644
--- a/packages/host/tests/acceptance/code-submode/playground-test.gts
+++ b/packages/host/tests/acceptance/code-submode/playground-test.gts
@@ -587,6 +587,51 @@ export class BlogPost extends CardDef {
     ]);
   });
 
+  test<TestContextWithSSE>('can create new instance', async function (assert) {
+    window.localStorage.removeItem('recent-files');
+    await visitOperatorMode({
+      submode: 'code',
+      codePath: `${testRealmURL}blog-post.gts`,
+    });
+    let recentFiles = JSON.parse(window.localStorage.getItem('recent-files')!);
+    assert.deepEqual(recentFiles[0], [testRealmURL, 'blog-post.gts']);
+    await click('[data-boxel-selector-item-text="BlogPost"]');
+    await click('[data-test-accordion-item="playground"] button');
+    assert
+      .dom('[data-test-instance-chooser] [data-test-selected-item]')
+      .doesNotExist();
+
+    await click('[data-test-instance-chooser]');
+    await this.expectEvents({
+      assert,
+      realm,
+      expectedNumberOfEvents: 2,
+      callback: async () => {
+        await click('[data-test-create-instance]');
+      },
+    });
+    recentFiles = JSON.parse(window.localStorage.getItem('recent-files')!);
+    assert.strictEqual(recentFiles.length, 2, 'recent file count is correct');
+    let newCardId = `${recentFiles[0][0]}${recentFiles[0][1]}`.replace(
+      '.json',
+      '',
+    );
+    assert
+      .dom('[data-test-instance-chooser] [data-test-selected-item]')
+      .hasText('Untitled Blog Post', 'created instance is selected');
+    assert
+      .dom(
+        `[data-test-playground-panel] [data-test-card="${newCardId}"][data-test-card-format="edit"]`,
+      )
+      .exists('new card is rendered in edit format');
+
+    await click('[data-test-instance-chooser]');
+    assert
+      .dom('[data-option-index]')
+      .exists({ count: 1 }, 'dropdown instance count is correct');
+    assert.dom('[data-option-index]').containsText('Blog Post');
+  });
+
   test<TestContextWithSSE>('playground preview for card with contained fields can live update when module changes', async function (assert) {
     // change: added "Hello" before rendering title on the template
     const authorCard = `import { contains, field, CardDef, Component } from "https://cardstack.com/base/card-api";